React中hook函數(shù)與useState及useEffect的使用
1. 簡介
在 React 的世界中,有容器組件和 UI 組件之分,在 React Hooks 出現(xiàn)之前,UI 組件我們可以使用函數(shù)組件,無狀態(tài)組件來展示 UI,而對于容器組件,函數(shù)組件就顯得無能為力,我們依賴于類組件來獲取數(shù)據(jù),處理數(shù)據(jù),并向下傳遞參數(shù)給 UI 組件進(jìn)行渲染。React在v16.8 的版本中推出了 React Hooks 新特性,Hook是一套工具函數(shù)的集合,它增強(qiáng)了函數(shù)組件的功能,hook不等于函數(shù)組件,所有的hook函數(shù)都是以use開頭。
使用 React Hooks 相比于從前的類組件有以下幾點好處:
- 代碼可讀性更強(qiáng),原本同一塊功能的代碼邏輯被拆分在了不同的生命周期函數(shù)中,容易使開發(fā)者修改代碼時不易去查找,通過 React Hooks 可以將功能代碼聚合,方便維護(hù)
- 組件樹層級變淺,在原本的代碼中,我們經(jīng)常使用 HOC/render/Props 等方式來復(fù)用組件的狀態(tài),增強(qiáng)功能等,無疑增加了組件樹層數(shù)及渲染,而在 React Hooks 中,這些功能都可以通過強(qiáng)大的自定義的 Hooks 來實現(xiàn)
使用hook限制:
- hook 只能用在函數(shù)組件中,class 組件不行
- 普通函數(shù)不能使用 hook,默認(rèn)只能是函數(shù)組件才能用
例外:普通函數(shù)名稱以 use 開頭也可以,(自定義的函數(shù)以 use 開頭,稱為自定義 hook)
- 函數(shù)組件內(nèi)部的函數(shù)也不能使用 hook
- hook 函數(shù)一定要放在函數(shù)組件的第一層,別放在 if/for 中(塊級作用域)
- 要求函數(shù)組件名稱必須首字母大寫
2. useState使用
概述:
類組件中有一個狀態(tài)屬性,可以通過此特殊屬性完成私有數(shù)據(jù)的操作。操作此 state 數(shù)據(jù)可以觸發(fā)視圖更新(this.setState()
)。
函數(shù)組件中,從 react16.8 之后,提供一個 hook 函數(shù) useState 方法,它可以模擬出類組件中的狀態(tài)。
語法:
let [變量,函數(shù)] = useState(值|()=>值)
變量就可以得到useState中的值,函數(shù)就可以修改值。值的存儲使用了閉包。
使用:
import React, { useState } from 'react'; const App = () => { // 相當(dāng)于在App函數(shù)組件是定義一個state數(shù)據(jù),變量名為 count,修改此count的值的方法為setCount // 寫法1:值 // let [count, setCount] = useState(100) // 有的時候,在項目中的初始數(shù)據(jù),要經(jīng)過一系列的運算才能出來的初始值,這時候就可以使用函數(shù)的寫法 // 寫法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ā)處理 -- 推薦寫法,這樣寫數(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完成表單項自定義hook函數(shù):
如果我們有兩個 input 框需要變?yōu)槭芸亟M件,我們可以這樣寫:
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 部分的代碼太過復(fù)雜,我們可以使用自定義 hook 函數(shù)來簡化這部分的代碼:
import React, { useState } from 'react'; // 在react中,定義的函數(shù)是以use開頭,則認(rèn)為它就是一個自定義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ù)拆分到另一個文件中:
useInput.js:
import { useState } from 'react'; // 在react中,定義的函數(shù)是以use開頭,則認(rèn)為它就是一個自定義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ù)組件對于在一些生命周期中操作還是無能為力,所以 React 提供了 useEffect 來幫助開發(fā)者處理函數(shù)組件,來幫助模擬完成一部份的開發(fā)中非常常用的生命周期方法。常被別的稱為:副作用處理函數(shù)。此函數(shù)的操作是異步的。
它并不能模擬全部的鉤子函數(shù),它只能模擬下面這幾個:componentDidMount、componentDidUpdate、componentWillUnmout。
注意:useEffect中不能有返回值,React它要自動回收
比如說下面這種場景,我們希望 console.log 函數(shù)中的內(nèi)容只打印一次,但是每當(dāng)試圖更新的時候,console.log 都會重新執(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;
這時候我們就可以使用 useEffect 函數(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(這種寫法可以模擬網(wǎng)絡(luò)請求)
import React, { useState, useEffect } from 'react'; const App = () => { let [count, setCount] = useState(100) const addCount = () => setCount(count + 1) // 模擬:componentDidMount(可以調(diào)用多次) // 參數(shù)2:依賴項,如果為空數(shù)據(jù),則只執(zhí)行1次 // 一般在這樣的寫法中,完成網(wǎng)絡(luò)請求 useEffect(() => { console.log('App -- useEffect'); }, []) return ( <div> <h3>App組件 --- {count}</h3> <button onClick={addCount}>++++</button> </div> ); } export default App;
利用依賴項,模擬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àn)相同的功能 // let [count, setCount] = useState({ num: 100 }) // const addCount = () => setCount({ num: count.num + 1 }) // const addCount = () => setCount(v => ({ num: v.num + 1 })) // 參數(shù)2中依賴項,進(jìn)行填值,只要依賴項中的值,發(fā)生改變,則進(jìn)行觸發(fā) // componentDidMount componentDidUpdate useEffect(() => { console.log('App -- useEffect'); // 這里的依賴項中只填了count,所以只有count發(fā)生改變,才會觸發(fā)當(dāng)前函數(shù) }, [count]) // 對依賴項的使用,可以像下面這樣分開寫,也可以寫在同一個數(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;
有依賴項,模擬: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) // 有依賴項,只要count改變,則觸發(fā) // componentDidMount componentDidUpdate componentWillUnmout useEffect(() => { console.log('App -- useEffect'); // 返回回調(diào)函數(shù)中就是 componetWillUnMount // 在執(zhí)行下一個 effect 之前,上一個 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ò)請求
import React, { useState, useEffect } from 'react'; import { get } from '@/utils/http' const App = () => { let [films, setFilms] = useState([]) // useEffect // 在之前的版本:第1個參數(shù)要求只能是一個普通的函數(shù),沒有返回對象,可以返回回調(diào)函數(shù),回調(diào)函數(shù)它模擬生命周期componentWillUnmout // 還有最后一個問題。在代碼中,我們使用async / await從第三方API獲取數(shù)據(jù)。如果你對async/await熟悉的話,你會知道,每個async函數(shù)都會默認(rèn)返回一個隱式的promise。但是,useEffect不應(yīng)該返回任何內(nè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ò)請求:
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ò)請求時,實現(xiàn)分頁:
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() }}>下一頁</button> </div > ); } export default App;
到此這篇關(guān)于React中hook函數(shù)與useState及useEffect的使用的文章就介紹到這了,更多相關(guān)React中hook函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React?中使用?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中實現(xiàn)修改input的defaultValue
這篇文章主要介紹了react中實現(xiàn)修改input的defaultValue方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05