網站首頁 編程語言 正文
前言
React-hooks
正式發布以后, useState
可以使函數組件像類組件一樣擁有 state
,也就說明函數組件可以通過 useState
改變 UI 視圖。那么 useState
到底應該如何使用,底層又是怎么運作的呢,首先一起看一下 useState
。
基本用法
[ state , dispatch ] = useState(initData)
- state,目的提供給 UI ,作為渲染視圖的數據源。
- dispatch 改變 state 的函數,可以理解為推動函數組件渲染的渲染函數。
- initData 有兩種情況,第一種情況是非函數,將作為 state 初始化的值。 第二種情況是函數,函數的返回值作為 useState 初始化的值。
initData為非函數的情況
/* 此時將把 0 作為初使值 */const [ num , setNum ] = useState(0)
initData為函數的情況
每當 React
重新渲染組件時,都會執行useState(initData)
。 如果初始狀態是原始值(數字,布爾值等),則不會有性能問題。
當初始狀態需要昂貴的性能方面的操作時,可以通過為useState(computeInitialState)
提供一個函數來使用狀態的延遲初始化,如下所示:
function MyComponent({ bigJsonData }) { const [value, setValue] = useState(function getInitialData() { const object = JSON.parse(bigJsonData); return object.initialValue; }); }
getInitialData()
僅在初始渲染時執行一次,以獲得初始狀態。在以后的組件渲染中,不會再調用getInitialData()
,從而跳過昂貴的操作。
state變化監聽
類組件 setState
中,有第二個參數 callback
或者是生命周期 componentDidUpdat
可以檢測監聽到 state
改變或是組件更新。
那么在函數組件中,如何怎么監聽 state
變化呢?這個時候就需要 useEffect
出場了,通常可以把 state
作為依賴項傳入 useEffect
第二個參數 deps
,但是注意 useEffect
初始化會默認執行一次。
具體可以參考如下 Demo :
import { useState, useEffect } from "react"; export default function App() { const [num, setNum] = useState(0); /* 監聽 num 變化 */ useEffect(() => { console.log("監聽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
監聽num變化,此時的num是: ?0
1
監聽num變化,此時的num是: ?1
3
監聽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
原因很簡單,函數組件更新就是函數的執行,在函數一次執行過程中,函數內部所有變量重新聲明,所以改變的 state ,只有在下一次函數組件執行時才會被更新。所以在如上同一個函數執行上下文中,number 一直為0,無論怎么打印,都拿不到最新的 state 。
我們只需要記住:在本次函數執行上下文中,是獲取不到最新的 state 值的就可以了
過時狀態問題
閉包是一個從外部作用域捕獲變量的函數。
閉包(例如事件處理程序,回調)可能會從函數組件作用域中捕獲狀態變量。 由于狀態變量在渲染之間變化,因此閉包應捕獲具有最新狀態值的變量。否則,如果閉包捕獲了過時的狀態值,則可能會遇到過時的狀態問題。
來看看一個過時的狀態是如何表現出來的。組件<DelayedCount>
延遲3秒計數按鈕點擊的次數
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
變量不能正確記錄實際點擊次數,有些點擊被吃掉。
delay()
是一個過時的閉包,它從初始渲染(使用0初始化時)中捕獲了過時的count變量。
為了解決這個問題,使用函數方法來更新count
狀態:
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> ); }
現在setCount(count => count + 1)
在delay()
中正確更新計數狀態。React
確保將最新狀態值作為參數提供給更新狀態函數,過時閉包的問題解決了。
快速單擊按鈕。 延遲過去后,count
能正確表示點擊次數。
更新引用數據類型
在使用 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
,發現 state
相同,不會開啟更新調度任務;demo 中兩次 state 指向了相同的內存空間,所以默認為 state 相等,就不會發生視圖更新了。
解決方法:
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 實現原理
下面簡單寫一下useState的實現原理:
function useState(init) { let state; // useState無法保存函數 if(typeof init === 'function') { state = init() } else { state = init } const setState = (change) => { // 判斷一下是否傳遞過來的是函數 if(typeof change === 'function') { // 如果是函數,調用,并將之前的state傳過去,接收到的返回值作為新的state并賦值 state = change(state) } else { // 如果不是函數,直接賦值 state = change; } } return [state, setState] }
原文鏈接:https://blog.csdn.net/qq_49900295/article/details/128067263
相關推薦
- 2022-09-12 C++數據結構哈希表詳解_C 語言
- 2023-01-26 Redis中如何設置日志_Redis
- 2022-12-10 Android入門之ScrollView的使用教程_Android
- 2023-01-30 delphi?判斷字符串是否為純數字組合_Delphi
- 2023-02-03 C++中的HTTP協議問題_C 語言
- 2022-04-03 C++中的運算符重載詳解_C 語言
- 2022-05-29 一起來看看C語言的預處理注意點_C 語言
- 2022-09-16 Pandas索引排序?df.sort_index()的實現_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支