日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

React?useState超詳細講解用法_React

作者:YinJie… ? 更新時間: 2022-12-25 編程語言

前言

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 能正確表示點擊次數。

更新引用數據類型

在使用 useStatedispatchAction 更新 state 的時候,記得不要傳入相同的 state,這樣會使視圖不更新:

const textObj = {name:'yinjie'}
const [useState1, setUseState1] = useState(textObj)
setUseState1((oldUseState1) => {
	oldUseState1.name = 'xxx'
    return oldUseState1
}
useEffect(() => {
	console.log(useState1)  
},[useState1])
//結果是沒有任何反應

為什么會造成這個原因呢?

useStatedispatchAction 處理邏輯中,會淺比較兩次 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

欄目分類
最近更新