react中使用usestate踩坑及解決
usestate的常規(guī)用法
在react框架中,不適用類組件,使用函數(shù)式組件又想自定義數(shù)據(jù)維護(hù)業(yè)務(wù)開發(fā)的時(shí)候,就需要使用react提供的hook來完成。usestate就是最常見的一種hook。
const [name,setName] = useState('dx'); setName('dx1')
中括號(hào)實(shí)際是一個(gè)解構(gòu)運(yùn)算,第一個(gè)name是設(shè)置的值,第二個(gè)setName是只能用來改變name的方法。
useState遇到的坑
1、useState不適合復(fù)雜對(duì)象的更改
因?yàn)閡seState不能像setState那樣進(jìn)行合并更新,當(dāng)使用useState第二個(gè)參數(shù)進(jìn)行數(shù)據(jù)更新的時(shí)候,必須傳入一個(gè)完整的結(jié)構(gòu),而不僅僅只是改變的那一部分。
如果你想讓一個(gè)復(fù)雜的對(duì)象都能實(shí)現(xiàn)響應(yīng),可以分兩種情況。
第一種情況,這個(gè)復(fù)雜的對(duì)象每次都是整體發(fā)生改變,那么也可以直接使用useState。
第二種情況,你只是想讓許多的簡(jiǎn)單數(shù)據(jù)都放到一個(gè)對(duì)象里面,這樣便于統(tǒng)一管理,那我建議,如果這些簡(jiǎn)單數(shù)據(jù)之間都沒什么必然聯(lián)系的話,還是分開創(chuàng)建多個(gè)state更好。
在編碼的過程中,我們寧愿以空間復(fù)雜度換取時(shí)間復(fù)雜度,多創(chuàng)建幾個(gè)變量和創(chuàng)建一個(gè)變量,在用戶體驗(yàn)上并不會(huì)有太多的差別。
但如果數(shù)據(jù)過于復(fù)雜,diff算法找到對(duì)應(yīng)的變化及發(fā)生響應(yīng),大規(guī)模的重新渲染,這一過程,將會(huì)導(dǎo)致用戶體驗(yàn)下降。
2、useState異步回調(diào)的問題
當(dāng)使用usestate對(duì)數(shù)據(jù)進(jìn)行更新,并不能立刻獲取到最新的數(shù)據(jù)。
? const [name, setName] = useState('dx'); ? const handleTest = () => { ? ? console.log(name) // dx ? ? setName('dx1') ? ? console.log(name) // dx ? }
解決的辦法。
一、配合useEffect使用
? const [name, setName] = useState('dx'); ? const handleTest = () => { ? ? console.log(name) //dx ? ? setName('dx1') ? ? console.log(name)//dx ? } ?? ? useEffect(() => { ? ? console.log(name) //dx1 ? },[name])
二、創(chuàng)建一個(gè)新的變量保存最新的數(shù)據(jù)
? const [name, setName] = useState('dx'); ? const handleTest = () => { ? ? console.log(name) //dx ? ? const newName = "dx1" ? ? setName(newName) ? ? console.log(newName) //dx1 ? }
三、用一個(gè)函數(shù)包裹,不推薦使用,因?yàn)楹瘮?shù)里面所有的東西都會(huì)全部重新定義
const [name, setName] = useState('dx'); function text () { const handleTest = () => { console.log(name) //dx const newName = "dx1" setName(newName) console.log(name) //dx console.log(newName) //dx1 } useEffect(() => { console.log(name) //dx1 },[name]) return ( <div> {name} //點(diǎn)擊之前dx,點(diǎn)擊按鈕之后dx1 <button type="button" onClick={handleTest }>改變名字</button> </div> ) } console.log(name) //點(diǎn)擊按鈕之前dx,點(diǎn)擊按鈕之后dx1
3、根據(jù)hook的規(guī)則,使用useState的位置有限制
強(qiáng)調(diào),所有的hook和自定義hook都遵循此規(guī)則。
僅頂層調(diào)用 Hook :不能在循環(huán),條件,嵌套函數(shù)等中調(diào)用useState()。
在多個(gè)useState()調(diào)用中,渲染之間的調(diào)用順序必須相同。
僅從React 函數(shù)調(diào)用 Hook:必須僅在函數(shù)組件或自定義鉤子內(nèi)部調(diào)用useState()。
4、使用useState,回調(diào)函數(shù)形式更改數(shù)據(jù)
const [a, setA] = useState({c:1}) /** oldA為之前的a,return為設(shè)置的新值 */ setA((oldA) => { return {c: oldA.c + 1} })
5、useState存入的值只是該值的引用(引用類型)
const textObj = {name:'dx'} const [useState1, setUseState1] = useState(textObj ) const [useState2, setUseState2] = useState(textObj ) /** usestate的操作不要放在函數(shù)的最外層,這里只是簡(jiǎn)單的代碼展示,你可以將set操作放在某個(gè)函數(shù)里面 */ setUseState1((oldUseState1) => { oldUseState1.age = 18 return {...oldUseState1} }) useEffect(() => { /** 改變一個(gè)會(huì)導(dǎo)致兩個(gè)都改變,深淺拷貝的問題 */ console.log(useState1) // {name: "dx", age: 18} console.log(useState2) // {name: "dx", age: 18} },[ useState1 ])
解決的方案
const textObj = {name:'dx'} const [useState1, setUseState1] = useState(textObj ) const [useState2, setUseState2] = useState(JSON.parse(JSON.stringify(textObj))) /** usestate的操作不要放在函數(shù)的最外層,這里只是簡(jiǎn)單的代碼展示,你可以將set操作放在某個(gè)函數(shù)里面 */ setUseState1((oldUseState1) => { oldUseState1.age = 18 return {...oldUseState1} }) useEffect(() => { /** 改變一個(gè)會(huì)導(dǎo)致兩個(gè)都改變,深淺拷貝的問題 */ console.log(useState1) // {name: "dx", age: 18} console.log(useState2) // {name: "dx"} },[ useState1 ])
6、useState,如果保存引用數(shù)據(jù),useEffect檢測(cè)不到變化?
const textObj = {name:'dx'} const [useState1, setUseState1] = useState(textObj) /** usestate的操作不要放在函數(shù)的最外層,這里只是簡(jiǎn)單的代碼展示,你可以將set操作放在某個(gè)函數(shù)里面 */ setUseState1((oldUseState1) => { oldUseState1.age = 18 return oldUseState1 useEffect(() => { console.log(useState1) },[ useState1 ]) //結(jié)果是沒有任何反應(yīng)
解決方法
const textObj = {name:'dx'} const [useState1, setUseState1] = useState(textObj) /** usestate的操作不要放在函數(shù)的最外層,這里只是簡(jiǎn)單的代碼展示,你可以將set操作放在某個(gè)函數(shù)里面 */ setUseState1((oldUseState1) => { oldUseState1.age = 18 /** 返回一個(gè)新的對(duì)象,useEffectc才能檢測(cè)得到 */ return {...oldUseState1} useEffect(() => { console.log(useState1) // {name: "dx", age: 18} },[ useState1 ])
7、useState無法保存一個(gè)函數(shù)
你是否嘗試著將函數(shù)的引用作為一個(gè)變量保存到useState中去呢?
比如:
const testFunciton1 = () => { console.log({name: 'dx',age: '18'}) } /** usestate保存函數(shù)測(cè)試 */ const [stateFunction, setstateFunction] = useState<() => void>(testFunciton1); useEffect(() => { console.log(stateFunction) }, [stateFunction])
打印結(jié)果
代碼中從未調(diào)用過testFunciton1 ,結(jié)果testFunciton1卻被執(zhí)行了
useEffect打印出來的卻是一個(gè)undefined。
稍微改動(dòng)一下代碼,再測(cè)試
const testFunciton1 = () => { console.log({name: 'dx',age: '18'}) return { name: 'yx', age: '17' } } /** usestate保存函數(shù)測(cè)試 */ const [stateFunction, setstateFunction] = useState<() => void>(testFunciton1); useEffect(() => { console.log(stateFunction) }, [stateFunction])
結(jié)果
很明顯,在useState中,函數(shù)會(huì)自動(dòng)調(diào)用,并且保存函數(shù)返回的值,而不能保存函數(shù)本身。
解決的方案
使用useState不能保存函數(shù),那么可以使用useCallback這個(gè)hook。
/** useCallback,使用參數(shù)與useEffect一致 */ const testFunction = useCallback(() => { // useCallback返回的函數(shù)在useCallbak中構(gòu)建 const testFunciton1 = () => { console.log({ name: 'dx', age: '18' }); return { name: 'yx', age: '17', }; }; return testFunciton1; }, []); useEffect(() => { console.log(testFunction()); }, [testFunction]);
結(jié)果
useState實(shí)現(xiàn)原理
前一段時(shí)間面試某大廠的時(shí)候,講到了useState這個(gè)hook,要求簡(jiǎn)單寫一下useState的實(shí)現(xiàn)原理,以下代碼只是一個(gè)粗淺的原理。
function useState(init) { let state; // useState無法保存函數(shù) if(typeof init === 'function') { state = init() } else { state = init } const setState = (change) => { // 判斷一下是否傳遞過來的是函數(shù) if(typeof change === 'function') { // 如果是函數(shù),調(diào)用,并將之前的state傳過去,接收到的返回值作為新的state并賦值 state = change(state) } else { // 如果不是函數(shù),直接賦值 state = change; } } return [state, setState] }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于React狀態(tài)管理的三個(gè)規(guī)則總結(jié)
隨著 JavaScript 單頁應(yīng)用開發(fā)日趨復(fù)雜,JavaScript 需要管理比任何時(shí)候都要多的 state (狀態(tài)),這篇文章主要給大家介紹了關(guān)于React狀態(tài)管理的三個(gè)規(guī)則,需要的朋友可以參考下2021-07-07React18之update流程從零實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了React18之update流程從零實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01React星星評(píng)分組件的實(shí)現(xiàn)
評(píng)分插件在購物的應(yīng)用中經(jīng)??梢钥吹玫剑怯弥鴦e人的總是沒有自己寫的順手,本文就使用React實(shí)現(xiàn)星星評(píng)分組件,感興趣的可以了解一下2021-06-06react ant protable自定義實(shí)現(xiàn)搜索下拉框
這篇文章主要介紹了react ant protable自定義實(shí)現(xiàn)搜索下拉框,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06react的ui庫antd中form表單使用SelectTree反顯問題及解決
這篇文章主要介紹了react的ui庫antd中form表單使用SelectTree反顯問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01