React useState超詳細講解用法
前言
React-hooks 正式發(fā)布以后, useState 可以使函數(shù)組件像類組件一樣擁有 state,也就說明函數(shù)組件可以通過 useState 改變 UI 視圖。那么 useState 到底應該如何使用,底層又是怎么運作的呢,首先一起看一下 useState 。
基本用法
[ state , dispatch ] = useState(initData)
- state,目的提供給 UI ,作為渲染視圖的數(shù)據(jù)源。
- dispatch 改變 state 的函數(shù),可以理解為推動函數(shù)組件渲染的渲染函數(shù)。
- initData 有兩種情況,第一種情況是非函數(shù),將作為 state 初始化的值。 第二種情況是函數(shù),函數(shù)的返回值作為 useState 初始化的值。
initData為非函數(shù)的情況
/* 此時將把 0 作為初使值 */const [ num , setNum ] = useState(0)
initData為函數(shù)的情況
每當 React 重新渲染組件時,都會執(zhí)行useState(initData)。 如果初始狀態(tài)是原始值(數(shù)字,布爾值等),則不會有性能問題。
當初始狀態(tài)需要昂貴的性能方面的操作時,可以通過為useState(computeInitialState)提供一個函數(shù)來使用狀態(tài)的延遲初始化,如下所示:
function MyComponent({ bigJsonData }) {
const [value, setValue] = useState(function getInitialData() {
const object = JSON.parse(bigJsonData);
return object.initialValue;
});
}
getInitialData()僅在初始渲染時執(zhí)行一次,以獲得初始狀態(tài)。在以后的組件渲染中,不會再調用getInitialData(),從而跳過昂貴的操作。
state變化監(jiān)聽
類組件 setState 中,有第二個參數(shù) callback 或者是生命周期 componentDidUpdat 可以檢測監(jiān)聽到 state 改變或是組件更新。
那么在函數(shù)組件中,如何怎么監(jiān)聽 state 變化呢?這個時候就需要 useEffect 出場了,通??梢园?state 作為依賴項傳入 useEffect 第二個參數(shù) deps ,但是注意 useEffect 初始化會默認執(zhí)行一次。
具體可以參考如下 Demo :
import { useState, useEffect } from "react";
export default function App() {
const [num, setNum] = useState(0);
/* 監(jiān)聽 num 變化 */
useEffect(() => {
console.log("監(jiān)聽num變化,此時的num是: " + num);
}, [num]);
const handerClick = () => {
setNum(1);
setTimeout(() => {
setNum(3);
});
};
console.log(num);
return (
<div>
<span> {num}</span>
<button onClick={handerClick}>num++</button>
</div>
);
}點擊按鈕后輸出:
0
監(jiān)聽num變化,此時的num是: 0
1
監(jiān)聽num變化,此時的num是: 1
3
監(jiān)聽num變化,此時的num是: 3
我們再把上面的demo改成這樣,看看會輸出什么:
const [ num , setNum ] = React.useState(0)
const handleClick = ()=>{
setNum(1)
console.log(num)
setTimeout(()=>{
setNum(3)
console.log(num)
})
}
結果:0 0 0
原因很簡單,函數(shù)組件更新就是函數(shù)的執(zhí)行,在函數(shù)一次執(zhí)行過程中,函數(shù)內部所有變量重新聲明,所以改變的 state ,只有在下一次函數(shù)組件執(zhí)行時才會被更新。所以在如上同一個函數(shù)執(zhí)行上下文中,number 一直為0,無論怎么打印,都拿不到最新的 state 。
我們只需要記?。涸诒敬魏瘮?shù)執(zhí)行上下文中,是獲取不到最新的 state 值的就可以了
過時狀態(tài)問題
閉包是一個從外部作用域捕獲變量的函數(shù)。
閉包(例如事件處理程序,回調)可能會從函數(shù)組件作用域中捕獲狀態(tài)變量。 由于狀態(tài)變量在渲染之間變化,因此閉包應捕獲具有最新狀態(tài)值的變量。否則,如果閉包捕獲了過時的狀態(tài)值,則可能會遇到過時的狀態(tài)問題。
來看看一個過時的狀態(tài)是如何表現(xiàn)出來的。組件<DelayedCount>延遲3秒計數(shù)按鈕點擊的次數(shù)
import React, { useState } from 'react';
function DelayedCount() {
const [count, setCount] = useState(0);
const handleClickAsync = () => {
setTimeout(function delay() {
setCount(count + 1);
}, 3000);
}
return (
<div>
{count}
<button onClick={handleClickAsync}>Increase async</button>
</div>
);
}快速多次點擊按鈕。count 變量不能正確記錄實際點擊次數(shù),有些點擊被吃掉。
delay() 是一個過時的閉包,它從初始渲染(使用0初始化時)中捕獲了過時的count變量。
為了解決這個問題,使用函數(shù)方法來更新count狀態(tài):
import React, { useState } from 'react';
function DelayedCount() {
const [count, setCount] = useState(0);
const handleClickAsync = () => {
setTimeout(function delay() {
setCount(count => count + 1);
}, 3000);
}
return (
<div>
{count}
<button onClick={handleClickAsync}>Increase async</button>
</div>
);
}現(xiàn)在setCount(count => count + 1)在delay()中正確更新計數(shù)狀態(tài)。React 確保將最新狀態(tài)值作為參數(shù)提供給更新狀態(tài)函數(shù),過時閉包的問題解決了。
快速單擊按鈕。 延遲過去后,count 能正確表示點擊次數(shù)。
更新引用數(shù)據(jù)類型
在使用 useState 的 dispatchAction 更新 state 的時候,記得不要傳入相同的 state,這樣會使視圖不更新:
const textObj = {name:'yinjie'}
const [useState1, setUseState1] = useState(textObj)
setUseState1((oldUseState1) => {
oldUseState1.name = 'xxx'
return oldUseState1
}
useEffect(() => {
console.log(useState1)
},[useState1])
//結果是沒有任何反應為什么會造成這個原因呢?
在 useState 的 dispatchAction 處理邏輯中,會淺比較兩次 state ,發(fā)現(xiàn) state 相同,不會開啟更新調度任務;demo 中兩次 state 指向了相同的內存空間,所以默認為 state 相等,就不會發(fā)生視圖更新了。
解決方法:
const textObj = {name:'yinjie'}
const [useState1, setUseState1] = useState(textObj)
setUseState1((oldUseState1) => {
oldUseState1.name = 'xxx'
/** 返回一個新的對象,useEffectc才能檢測得到 */
return {...oldUseState1}
}
useEffect(() => {
console.log(useState1) // {name: "xxx"}
},[useState1])在上面的 demo 中我們淺拷貝了對象,重新申請了一個內存空間。
useState 實現(xiàn)原理
下面簡單寫一下useState的實現(xiàn)原理:
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ù),調用,并將之前的state傳過去,接收到的返回值作為新的state并賦值
state = change(state)
} else {
// 如果不是函數(shù),直接賦值
state = change;
}
}
return [state, setState]
}到此這篇關于React useState超詳細講解用法的文章就介紹到這了,更多相關React useState內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
React Native中的RefreshContorl下拉刷新使用
本篇文章主要介紹了React Native中的RefreshContorl下拉刷新使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
react實現(xiàn)pure render時bind(this)隱患需注意!
這篇文章主要為大家詳細介紹了值得你在react實現(xiàn)pure render的時候,需要注意的bind(this)隱患,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03

