最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React中useState值為對(duì)象時(shí)改變值不渲染問(wèn)題

 更新時(shí)間:2023年02月13日 08:38:41   作者:luotouzi  
這篇文章主要介紹了React中useState值為對(duì)象時(shí)改變值不渲染問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

useState值為對(duì)象時(shí)改變值不渲染

問(wèn)題

修改State并重新setState(arr)后,值改變,但并未重新渲染

const [arr, setArr] = useState([])
arr.push(1)
setArr(arr)

原因

React中默認(rèn)淺監(jiān)聽(tīng),當(dāng)State值為對(duì)象時(shí),棧中存的是對(duì)象的引用(地址),setState改變的是堆中的數(shù)據(jù)

所以此時(shí) setArr(arr) 后,棧中的地址還是原地址,React淺監(jiān)聽(tīng)到地址沒(méi)變,故會(huì)認(rèn)為State并未改變,故沒(méi)有重渲染頁(yè)面

解決

思路:將棧中原arr所指向的地址改變即可

1)直接setState(要修改的值)

const [arr, setArr] = useState([])
setArr(1)

2)新創(chuàng)建一個(gè)數(shù)組newArr,將原數(shù)組的值賦值給新數(shù)組,并setState(newArr)

const [arr, setArr] = useState([])
const newArr = arr.slice(1)
setArr(newArr)

3)利用ES6的拓展符…

const [arr, setArr] = useState([])
setArr([...arr])

useState用法指南

useState

const [state, setState] = useState(initialState);

返回一個(gè) state,以及更新 state 的函數(shù)。

在初始渲染期間,返回的狀態(tài) (state) 與傳入的第一個(gè)參數(shù) (initialState) 值相同。

setState 函數(shù)用于更新 state。它接收一個(gè)新的 state 值并將組件的一次重新渲染加入隊(duì)列。

setState(newState);

在后續(xù)的重新渲染中,useState 返回的第一個(gè)值將始終是更新后最新的 state。

函數(shù)式更新

如果新的 state 需要通過(guò)使用先前的 state 計(jì)算得出,那么可以將函數(shù)傳遞給 setState。該函數(shù)將接收先前的 state,并返回一個(gè)更新后的值。下面的計(jì)數(shù)器組件示例展示了 setState 的兩種用法:

function Counter() {
? const [count, setCount] = useState(0);
? function handleClick() {
? ? setCount(count + 1)
? }
? function handleClickFn() {
? ? setCount((prevCount) => {
? ? ? return prevCount + 1
? ? })
? }
? return (
? ? <>
? ? ? Count: {count}
? ? ? <button onClick={handleClick}>+</button>
? ? ? <button onClick={handleClickFn}>+</button>
? ? </>
? );
}

兩種方式的區(qū)別

注意上面的代碼,handleClick和handleClickFn一個(gè)是通過(guò)一個(gè)新的 state 值更新,一個(gè)是通過(guò)函數(shù)式更新返回新的 state?,F(xiàn)在這兩種寫法沒(méi)有任何區(qū)別,但是如果是異步更新的話,那你就要注意了,他們是有區(qū)別的,來(lái)看下面例子:

function Counter() {
? const [count, setCount] = useState(0);
? function handleClick() {
? ? setTimeout(() => {
? ? ? setCount(count + 1)
? ? }, 3000);
? }
? function handleClickFn() {
? ? setTimeout(() => {
? ? ? setCount((prevCount) => {
? ? ? ? return prevCount + 1
? ? ? })
? ? }, 3000);
? }
? return (
? ? <>
? ? ? Count: {count}
? ? ? <button onClick={handleClick}>+</button>
? ? ? <button onClick={handleClickFn}>+</button>
? ? </>
? );
}

當(dāng)我設(shè)置為異步更新,點(diǎn)擊按鈕延遲到3s之后去調(diào)用setCount函數(shù),當(dāng)我快速點(diǎn)擊按鈕時(shí),也就是說(shuō)在3s多次去觸發(fā)更新,但是只有一次生效,因?yàn)?count 的值是沒(méi)有變化的。

當(dāng)使用函數(shù)式更新 state 的時(shí)候,這種問(wèn)題就沒(méi)有了,因?yàn)樗梢垣@取之前的 state 值,也就是代碼中的 prevCount 每次都是最新的值。

其實(shí)這個(gè)特點(diǎn)和類組件中 setState 類似,可以接收一個(gè)新的 state 值更新,也可以函數(shù)式更新。如果新的 state 需要通過(guò)使用先前的 state 計(jì)算得出,那么就要使用函數(shù)式更新。

因?yàn)閟etState更新可能是異步,當(dāng)你在事件綁定中操作 state 的時(shí)候,setState更新就是異步的。

class Counter extends React.Component {
? constructor(props) {
? ? super(props)
? ? this.state = { count: 0 }
? }
? handleClick = () => {
? ? this.setState({ count: this.state.count + 1 })
? ? this.setState({ count: this.state.count + 1 })
? ? // 這樣寫只會(huì)加1
? }
? handleClickFn = () => {
? ? this.setState((prevState) => {
? ? ? return { count: prevState.count + 1 }
? ? })
? ? this.setState((prevState) => {
? ? ? return { count: prevState.count + 1 }
? ? })
? }
? render() {
? ? return (
? ? ? <>
? ? ? ? Count: {this.state.count}
? ? ? ? <button onClick={this.handleClick}>+</button>
? ? ? ? <button onClick={this.handleClickFn}>+</button>
? ? ? </>
? ? );
? }
}

當(dāng)你在定時(shí)器中操作 state 的時(shí)候,而 setState 更新就是同步的。

class Counter extends React.Component {
? constructor(props) {
? ? super(props)
? ? this.state = { count: 0 }
? }
? handleClick = () => {
? ? setTimeout(() => {
? ? ? this.setState({ count: this.state.count + 1 })
? ? ? this.setState({ count: this.state.count + 1 })
? ? ? // 這樣寫是正常的,兩次setState最后是加2
? ? }, 3000);
? }
? handleClickFn = () => {
? ? this.setState((prevState) => {
? ? ? return { count: prevState.count + 1 }
? ? })
? ? this.setState((prevState) => {
? ? ? return { count: prevState.count + 1 }
? ? })
? }
? render() {
? ? return (
? ? ? <>
? ? ? ? Count: {this.state.count}
? ? ? ? <button onClick={this.handleClick}>+</button>
? ? ? ? <button onClick={this.handleClickFn}>+</button>
? ? ? </>
? ? );
? }
}

注意這里的同步和異步指的是 setState 函數(shù)。因?yàn)樯婕暗?state 的狀態(tài)合并,react 認(rèn)為當(dāng)你在事件綁定中操作 state 是非常頻繁的,所以為了節(jié)約性能 react 會(huì)把多次 setState 進(jìn)行合并為一次,最后在一次性的更新 state,而定時(shí)器里面操作 state 是不會(huì)把多次合并為一次更新的。

注意:與 class 組件中的 setState 方法不同,useState 不會(huì)自動(dòng)合并更新對(duì)象。

性能優(yōu)化

React 使用 Object.is 比較算法來(lái)比較 state。

在 React 應(yīng)用中,當(dāng)某個(gè)組件的狀態(tài)發(fā)生變化時(shí),它會(huì)以該組件為根,重新渲染整個(gè)組件子樹(shù)。

function Child({ onButtonClick, data }) {
? console.log('Child Render')
? return (
? ? <button onClick={onButtonClick}>{data.number}</button>
? )
}

function App() {
? const [number, setNumber] = useState(0)
? const [name, setName] = useState('hello') // 表單的值
? const addClick = () => setNumber(number + 1)
? const data = { number }
? return (
? ? <div>
? ? ? <input type="text" value={name} onChange={e => setName(e.target.value)} />
? ? ? <Child onButtonClick={addClick} data={data} />
? ? </div>
? )
}

如要避免不必要的子組件的重渲染,使用 React.memo 僅檢查 props 變更。 默認(rèn)情況下其只會(huì)對(duì)復(fù)雜對(duì)象做淺層對(duì)比。所有使用 memo 優(yōu)化后的代碼如下:

function Child({ onButtonClick, data }) {
? console.log('Child Render')
? return (
? ? <button onClick={onButtonClick}>{data.number}</button>
? )
}
Child = memo(Child); // 在這里優(yōu)化了
function App() {
? const [number, setNumber] = useState(0)
? const [name, setName] = useState('hello') // 表單的值
? const addClick = () => setNumber(number + 1)
? const data = { number }
? return (
? ? <div>
? ? ? <input type="text" value={name} onChange={e => setName(e.target.value)} />
? ? ? <Child onButtonClick={addClick} data={data} />
? ? </div>
? )
}

你以為代碼中的Child = memo(Child);已經(jīng)優(yōu)化了嗎,然而并沒(méi)有,當(dāng)你在更改了父組件的狀態(tài),子組件依然會(huì)重新渲染,因?yàn)檫@關(guān)系到了React是如何淺層比較的,在子組件中onButtonClick 和 data 都是引用類型,所以他們是始終都不相等的,也就是[]===[]這樣比較時(shí)始終返回false,在基本數(shù)據(jù)類型比較時(shí)memo才會(huì)起作用。

關(guān)于如何解決這個(gè)問(wèn)題,我們就要使用兩個(gè)新的API,useMemo和useCallback的Hook。下面是經(jīng)過(guò)優(yōu)化之后的代碼。

function Child({ onButtonClick, data }) {
? console.log('Child Render')
? return (
? ? <button onClick={onButtonClick}>{data.number}</button>
? )
}

Child = memo(Child)

function App() {
? const [number, setNumber] = useState(0)
? const [name, setName] = useState('hello') // 表單的值
? const addClick = useCallback(() => setNumber(number + 1), [number])
? const data = useMemo(() => ({ number }), [number])
? return (
? ? <div>
? ? ? <input type="text" value={name} onChange={e => setName(e.target.value)} />
? ? ? <Child onButtonClick={addClick} data={data} />
? ? </div>
? )
}

export default App;

把“創(chuàng)建”函數(shù)和依賴項(xiàng)數(shù)組作為參數(shù)傳入 useMemo,它僅會(huì)在某個(gè)依賴項(xiàng)改變時(shí)才重新計(jì)算 memoized 值。這種優(yōu)化有助于避免在每次渲染時(shí)都進(jìn)行高開(kāi)銷的計(jì)算。如果沒(méi)有提供依賴項(xiàng)數(shù)組,useMemo 在每次渲染時(shí)都會(huì)計(jì)算新的值。

useCallback返回一個(gè) memoized 回調(diào)函數(shù)。useCallback(fn, deps) 相當(dāng)于 useMemo(() => fn, deps)。

useCallback 和 useMemo 參數(shù)相同,第一個(gè)參數(shù)是函數(shù),第二個(gè)參數(shù)是依賴項(xiàng)的數(shù)組。主要區(qū)別是 React.useMemo 將調(diào)用 fn 函數(shù)并返回其結(jié)果,而 React.useCallback 將返回 fn 函數(shù)而不調(diào)用它。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • React中react-redux和路由詳解

    React中react-redux和路由詳解

    這篇文章主要介紹了React中react-redux和路由詳解,redux早期被設(shè)計(jì)成可以在各個(gè)框架中使用,因此在不同的框架中使用的時(shí)候,要引入相應(yīng)的插件,感興趣的朋友可以繼續(xù)學(xué)習(xí)下面文章
    2022-08-08
  • JavaScript React如何修改默認(rèn)端口號(hào)方法詳解

    JavaScript React如何修改默認(rèn)端口號(hào)方法詳解

    這篇文章主要介紹了JavaScript React如何修改默認(rèn)端口號(hào)方法詳解,文中通過(guò)步驟圖片解析介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • React+hook實(shí)現(xiàn)聯(lián)動(dòng)模糊搜索

    React+hook實(shí)現(xiàn)聯(lián)動(dòng)模糊搜索

    這篇文章主要為大家詳細(xì)介紹了如何利用React+hook+antd實(shí)現(xiàn)聯(lián)動(dòng)模糊搜索功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-02-02
  • react-native DatePicker日期選擇組件的實(shí)現(xiàn)代碼

    react-native DatePicker日期選擇組件的實(shí)現(xiàn)代碼

    本篇文章主要介紹了react-native DatePicker日期選擇組件的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,有興趣的可以了解下
    2017-09-09
  • React+Typescript項(xiàng)目環(huán)境搭建并使用redux環(huán)境的詳細(xì)過(guò)程

    React+Typescript項(xiàng)目環(huán)境搭建并使用redux環(huán)境的詳細(xì)過(guò)程

    這篇文章主要介紹了React+Typescript項(xiàng)目環(huán)境搭建并使用redux環(huán)境的詳細(xì)過(guò)程,本文通過(guò)圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • React錯(cuò)誤邊界Error Boundaries

    React錯(cuò)誤邊界Error Boundaries

    錯(cuò)誤邊界是一種React組件,這種組件可以捕獲發(fā)生在其子組件樹(shù)任何位置的JavaScript錯(cuò)誤,并打印這些錯(cuò)誤,同時(shí)展示降級(jí)UI,而并不會(huì)渲染那些發(fā)生崩潰的子組件樹(shù)
    2023-01-01
  • React Native自定義Android的SSL證書鏈校驗(yàn)

    React Native自定義Android的SSL證書鏈校驗(yàn)

    這篇文章主要為大家介紹了React Native自定義Android的SSL證書鏈校驗(yàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • 解決React報(bào)錯(cuò)JSX?element?type?does?not?have?any?construct?or?call?signatures

    解決React報(bào)錯(cuò)JSX?element?type?does?not?have?any?construct

    這篇文章主要為大家介紹了解決React報(bào)錯(cuò)JSX?element?type?does?not?have?any?construct?or?call?signatures,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • React使用useEffect解決setState副作用詳解

    React使用useEffect解決setState副作用詳解

    這篇文章主要為大家介紹了React使用useEffect解決setState副作用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • React useEffect的理解與使用

    React useEffect的理解與使用

    useEffect是react v16.8新引入的特性。我們可以把useEffect hook看作是componentDidMount、componentDidUpdate、componentWillUnmounrt三個(gè)函數(shù)的組合
    2022-12-12

最新評(píng)論

安远县| 云和县| 吉木乃县| 建德市| 莆田市| 仪陇县| 咸丰县| 喀喇| 疏勒县| 大厂| 县级市| 衡南县| 磐安县| 新河县| 大英县| 金坛市| 呈贡县| 双峰县| 故城县| 巴南区| 翼城县| 珠海市| 家居| 水城县| 浦东新区| 定南县| 白玉县| 交口县| 德化县| 酒泉市| 罗山县| 澜沧| 阜阳市| 惠来县| 安徽省| 若尔盖县| 顺义区| 岳池县| 福海县| 深泽县| 松溪县|