React中hook函數(shù)與useState及useEffect的使用
1. 簡(jiǎn)介
在 React 的世界中,有容器組件和 UI 組件之分,在 React Hooks 出現(xiàn)之前,UI 組件我們可以使用函數(shù)組件,無(wú)狀態(tài)組件來(lái)展示 UI,而對(duì)于容器組件,函數(shù)組件就顯得無(wú)能為力,我們依賴于類組件來(lái)獲取數(shù)據(jù),處理數(shù)據(jù),并向下傳遞參數(shù)給 UI 組件進(jìn)行渲染。React在v16.8 的版本中推出了 React Hooks 新特性,Hook是一套工具函數(shù)的集合,它增強(qiáng)了函數(shù)組件的功能,hook不等于函數(shù)組件,所有的hook函數(shù)都是以u(píng)se開(kāi)頭。
使用 React Hooks 相比于從前的類組件有以下幾點(diǎn)好處:
- 代碼可讀性更強(qiáng),原本同一塊功能的代碼邏輯被拆分在了不同的生命周期函數(shù)中,容易使開(kāi)發(fā)者修改代碼時(shí)不易去查找,通過(guò) React Hooks 可以將功能代碼聚合,方便維護(hù)
- 組件樹(shù)層級(jí)變淺,在原本的代碼中,我們經(jīng)常使用 HOC/render/Props 等方式來(lái)復(fù)用組件的狀態(tài),增強(qiáng)功能等,無(wú)疑增加了組件樹(shù)層數(shù)及渲染,而在 React Hooks 中,這些功能都可以通過(guò)強(qiáng)大的自定義的 Hooks 來(lái)實(shí)現(xiàn)
使用hook限制:
- hook 只能用在函數(shù)組件中,class 組件不行
- 普通函數(shù)不能使用 hook,默認(rèn)只能是函數(shù)組件才能用
例外:普通函數(shù)名稱以 use 開(kāi)頭也可以,(自定義的函數(shù)以 use 開(kāi)頭,稱為自定義 hook)
- 函數(shù)組件內(nèi)部的函數(shù)也不能使用 hook
- hook 函數(shù)一定要放在函數(shù)組件的第一層,別放在 if/for 中(塊級(jí)作用域)
- 要求函數(shù)組件名稱必須首字母大寫(xiě)
2. useState使用
概述:
類組件中有一個(gè)狀態(tài)屬性,可以通過(guò)此特殊屬性完成私有數(shù)據(jù)的操作。操作此 state 數(shù)據(jù)可以觸發(fā)視圖更新(this.setState()
)。
函數(shù)組件中,從 react16.8 之后,提供一個(gè) hook 函數(shù) useState 方法,它可以模擬出類組件中的狀態(tài)。
語(yǔ)法:
let [變量,函數(shù)] = useState(值|()=>值)
變量就可以得到useState中的值,函數(shù)就可以修改值。值的存儲(chǔ)使用了閉包。
使用:
import React, { useState } from 'react'; const App = () => { // 相當(dāng)于在App函數(shù)組件是定義一個(gè)state數(shù)據(jù),變量名為 count,修改此count的值的方法為setCount // 寫(xiě)法1:值 // let [count, setCount] = useState(100) // 有的時(shí)候,在項(xiàng)目中的初始數(shù)據(jù),要經(jīng)過(guò)一系列的運(yùn)算才能出來(lái)的初始值,這時(shí)候就可以使用函數(shù)的寫(xiě)法 // 寫(xiě)法2:函數(shù) let [count, setCount] = useState(() => 100) const addCount = () => { setCount(count + 1) } return ( <div> <h1>{count}</h1> <button onClick={addCount}>++++</button> </div> ); } export default App;
處理并發(fā)操作:
import React, { useState } from 'react'; const App = () => { let [count, setCount] = useState(() => 100) const addCount = () => { // 并發(fā)處理數(shù)據(jù)的完整性得不到保證 // setCount(count + 1) // 100+1 // setCount(count + 1) // 100+1 // setCount(count + 1) // 100+1 // 并發(fā)處理 -- 推薦寫(xiě)法,這樣寫(xiě)數(shù)據(jù)的完整性可靠的 setCount(v => v + 1) setCount(v => v + 1) setCount(v => v + 1) } return ( <div> <h1>{count}</h1> <button onClick={addCount}>++++</button> </div> ); } export default App;
使用useState完成表單項(xiàng)自定義hook函數(shù):
如果我們有兩個(gè) input 框需要變?yōu)槭芸亟M件,我們可以這樣寫(xiě):
import React, { useState } from 'react'; const App = () => { let [username, setUsername] = useState('') let [password, setPassword] = useState('') return ( <div> {/* 受控組件 */} <input type="text" value={username} onChange={e => setUsername(e.target.value)} /> <input type="text" value={password} onChange={e => setPassword(e.target.value)} /> </div> ); } export default App;
上面的做法讓 return 部分的代碼太過(guò)復(fù)雜,我們可以使用自定義 hook 函數(shù)來(lái)簡(jiǎn)化這部分的代碼:
import React, { useState } from 'react'; // 在react中,定義的函數(shù)是以u(píng)se開(kāi)頭,則認(rèn)為它就是一個(gè)自定義hook函數(shù) // 在自定義hook函數(shù)中,可以調(diào)用內(nèi)置hook function useInput(initialValue = '') { let [value, setValue] = useState(initialValue) return { value, onChange: e => setValue(e.target.value.trim()) } } const App = () => { let usernameInput = useInput('') let passwordInput = useInput('') return ( <div> {/* 受控組件 */} <input type="text" {...usernameInput} /> <input type="text" {...passwordInput} /> </div> ); } export default App;
我們還可以使用模塊化的思想,將自定義 hook 函數(shù)拆分到另一個(gè)文件中:
useInput.js:
import { useState } from 'react'; // 在react中,定義的函數(shù)是以u(píng)se開(kāi)頭,則認(rèn)為它就是一個(gè)自定義hook函數(shù) // 在自定義hook函數(shù)中,可以調(diào)用內(nèi)置hook const useInput = (initialValue = '') => { let [value, setValue] = useState(initialValue) return { value, onChange: e => setValue(e.target.value.trim()) } } export default useInput
App.jsx:
import React from 'react'; import useInput from './hook/useInput'; const App = () => { let usernameInput = useInput('') let passwordInput = useInput('') return ( <div> <input type="text" {...usernameInput} /> <input type="text" {...passwordInput} /> </div> ); } export default App;
3. useEffect使用
概述:
此 hook 可以模擬函數(shù)組件的生命周期,函數(shù)組件對(duì)于在一些生命周期中操作還是無(wú)能為力,所以 React 提供了 useEffect 來(lái)幫助開(kāi)發(fā)者處理函數(shù)組件,來(lái)幫助模擬完成一部份的開(kāi)發(fā)中非常常用的生命周期方法。常被別的稱為:副作用處理函數(shù)。此函數(shù)的操作是異步的。
它并不能模擬全部的鉤子函數(shù),它只能模擬下面這幾個(gè):componentDidMount、componentDidUpdate、componentWillUnmout。
注意:useEffect中不能有返回值,React它要自動(dòng)回收
比如說(shuō)下面這種場(chǎng)景,我們希望 console.log 函數(shù)中的內(nèi)容只打印一次,但是每當(dāng)試圖更新的時(shí)候,console.log 都會(huì)重新執(zhí)行:
import React, { useState } from 'react'; const App = () => { let [count, setCount] = useState(100) const addCount = () => setCount(count + 1) console.log('App -- 要求此處只打印一次'); return ( <div> <h3>App組件 --- {count}</h3> <button onClick={addCount}>++++</button> </div> ); } export default App;
這時(shí)候我們就可以使用 useEffect 函數(shù)來(lái)實(shí)現(xiàn)上述需求。
使用:
模擬:componentDidMount componentDidUpdate
import React, { useState, useEffect } from 'react'; const App = () => { let [count, setCount] = useState(100) const addCount = () => setCount(count + 1) // 模擬:componentDidMount componentDidUpdate(可以調(diào)用多次) useEffect(() => { console.log('App -- useEffect'); }) return ( <div> <h3>App組件 --- {count}</h3> <button onClick={addCount}>++++</button> </div> ); } export default App;
模擬:componentDidMount(這種寫(xiě)法可以模擬網(wǎng)絡(luò)請(qǐng)求)
import React, { useState, useEffect } from 'react'; const App = () => { let [count, setCount] = useState(100) const addCount = () => setCount(count + 1) // 模擬:componentDidMount(可以調(diào)用多次) // 參數(shù)2:依賴項(xiàng),如果為空數(shù)據(jù),則只執(zhí)行1次 // 一般在這樣的寫(xiě)法中,完成網(wǎng)絡(luò)請(qǐng)求 useEffect(() => { console.log('App -- useEffect'); }, []) return ( <div> <h3>App組件 --- {count}</h3> <button onClick={addCount}>++++</button> </div> ); } export default App;
利用依賴項(xiàng),模擬componentDidMount、componentDidUpdate
import React, { useState, useEffect } from 'react'; const App = () => { let [count, setCount] = useState(100) let [name, setName] = useState('') const addCount = () => setCount(count + 1) // 下面這種寫(xiě)法,也可以實(shí)現(xiàn)相同的功能 // let [count, setCount] = useState({ num: 100 }) // const addCount = () => setCount({ num: count.num + 1 }) // const addCount = () => setCount(v => ({ num: v.num + 1 })) // 參數(shù)2中依賴項(xiàng),進(jìn)行填值,只要依賴項(xiàng)中的值,發(fā)生改變,則進(jìn)行觸發(fā) // componentDidMount componentDidUpdate useEffect(() => { console.log('App -- useEffect'); // 這里的依賴項(xiàng)中只填了count,所以只有count發(fā)生改變,才會(huì)觸發(fā)當(dāng)前函數(shù) }, [count]) // 對(duì)依賴項(xiàng)的使用,可以像下面這樣分開(kāi)寫(xiě),也可以寫(xiě)在同一個(gè)數(shù)組中 // useEffect(() => { // console.log('App -- useEffect'); // }, [name]) return ( <div> <input value={name} onChange={e => setName(e.target.value)} /> <h3>App組件 --- {count}</h3> <button onClick={addCount}>++++</button> </div> ); } export default App;
有依賴項(xiàng),模擬:componentDidMount、componentDidUpdate、componentWillUnmout
import React, { useState, useEffect } from 'react'; const App = () => { let [count, setCount] = useState(100) let [name, setName] = useState('') const addCount = () => setCount(count + 1) // 有依賴項(xiàng),只要count改變,則觸發(fā) // componentDidMount componentDidUpdate componentWillUnmout useEffect(() => { console.log('App -- useEffect'); // 返回回調(diào)函數(shù)中就是 componetWillUnMount // 在執(zhí)行下一個(gè) effect 之前,上一個(gè) effect 就已被清除 return () => { console.log('componentWillUnmout'); } }, [count]) return ( <div> <input value={name} onChange={e => setName(e.target.value)} /> <h3>App組件 --- {count}</h3> <button onClick={addCount}>++++</button> </div> ); } export default App;
模擬組件銷毀
import React, { useState, useEffect } from 'react'; const App = () => { let [count, setCount] = useState(100) let [name, setName] = useState('') const addCount = () => setCount(count + 1) return ( <div> <input value={name} onChange={e => setName(e.target.value)} /> <h3>App組件 --- {count}</h3> { count > 103 ? null : <Child /> } <button onClick={addCount}>++++</button> </div> ); } function Child() { // componentDidMount componentWillUnmout -- 一般模擬組件的銷毀 useEffect(() => { console.log('child -- componentDidMount'); return () => { console.log('child -- componentWillUnmout'); } }, []) return ( <div> <h3>Child</h3> </div> ) } export default App;
useEffect發(fā)起網(wǎng)絡(luò)請(qǐng)求
import React, { useState, useEffect } from 'react'; import { get } from '@/utils/http' const App = () => { let [films, setFilms] = useState([]) // useEffect // 在之前的版本:第1個(gè)參數(shù)要求只能是一個(gè)普通的函數(shù),沒(méi)有返回對(duì)象,可以返回回調(diào)函數(shù),回調(diào)函數(shù)它模擬生命周期componentWillUnmout // 還有最后一個(gè)問(wèn)題。在代碼中,我們使用async / await從第三方API獲取數(shù)據(jù)。如果你對(duì)async/await熟悉的話,你會(huì)知道,每個(gè)async函數(shù)都會(huì)默認(rèn)返回一個(gè)隱式的promise。但是,useEffect不應(yīng)該返回任何內(nèi)容。這就是為什么會(huì)在控制臺(tái)日志中看到以下警告: // Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect useEffect(async () => { let ret = await get('/api/swiper') setFilms(ret.data) }, []) // 如果useEffect頂層不支持 async/await可以使用如下的解決方案 /* useEffect(() => { ; (async function () { let ret = await get('/api/swiper') setFilms(ret.data) })() }, []) */ return ( <div> <h3>App組件</h3> <ul> { films.map(item => ( <li key={item.id} > {item.title}</li> )) } </ul> </div > ); } export default App;
useEffect 封裝網(wǎng)絡(luò)請(qǐng)求:
useSwiper.js:
import { useEffect, useState } from 'react'; import { get } from '@/utils/http' const useSwiper = (initialValue = []) => { let [data, setData] = useState(initialValue) useEffect(async () => { let ret = await get('/api/swiper') setData(ret.data) }, []) return data } export default useSwiper
App.jsx:
import useSwiper from "./hook/useSwiper"; const App = () => { let data = useSwiper() return ( <div> <h3>App組件</h3> <ul> { data.map(item => ( <li key={item.id} > {item.title}</li> )) } </ul> </div > ); } export default App;
封裝網(wǎng)絡(luò)請(qǐng)求時(shí),實(shí)現(xiàn)分頁(yè):
useGoodFood.js:
import { useEffect, useState } from 'react'; import { get } from '@/utils/http' const useGoodFood = (initialValue = []) => { let [data, setData] = useState(initialValue) let [page, setPage] = useState(1) const loadData = async () => { let ret = await get('/api/goodfood?page=' + page) if (ret.data.length > 0) { // setData(v => [...v, ...ret.data]) setData(ret.data) setPage(v => v + 1) } } useEffect(() => { loadData() }, []) return [data, loadData] } export default useGoodFood
App.jsx:
import useGoodFood from "./hook/useGoodFood"; const App = () => { let [data, loadData] = useGoodFood() return ( <div> <h3>App組件</h3> <ul> { data.map(item => ( <li key={item.id} > {item.name}</li> )) } </ul> <hr /> <button onClick={() => { loadData() }}>下一頁(yè)</button> </div > ); } export default App;
到此這篇關(guān)于React中hook函數(shù)與useState及useEffect的使用的文章就介紹到這了,更多相關(guān)React中hook函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React實(shí)現(xiàn)二級(jí)聯(lián)動(dòng)的方法
這篇文章主要為大家詳細(xì)介紹了React實(shí)現(xiàn)二級(jí)聯(lián)動(dòng)的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09React?中使用?RxJS?優(yōu)化數(shù)據(jù)流的處理方案
這篇文章主要為大家介紹了React?中使用?RxJS?優(yōu)化數(shù)據(jù)流的處理方案示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02React特征Form?單向數(shù)據(jù)流示例詳解
這篇文章主要為大家介紹了React特征Form?單向數(shù)據(jù)流示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09react實(shí)現(xiàn)無(wú)限循環(huán)滾動(dòng)信息
這篇文章主要為大家詳細(xì)介紹了react實(shí)現(xiàn)無(wú)限循環(huán)滾動(dòng)信息,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10react中實(shí)現(xiàn)修改input的defaultValue
這篇文章主要介紹了react中實(shí)現(xiàn)修改input的defaultValue方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05