react使用useState修改對象或者數組的值無法改變視圖的問題
使用useState修改對象或者數組的值無法改變視圖
在react中使用useState無法改變視圖,數據改變但是視圖未改變
未渲染的代碼如下:
const [needLists,setNeedLists]=useState([]) const pressDownEnter=(e)=>{ if(e.keyCode===13){ needLists.push({ content:e.target.value, status:0 }) setNeedLists(needLists) e.target.value="" } }
未發生改變的原因可能是因為needLists本身並沒有變化,因為needLists變量存儲的地址是不變的
解決辦法
通過數據結構讓傳入的新值和原來的數組指向的不是同一片內存,就能觸發dom的更新
const [needLists,setNeedLists]=useState([]) const pressDownEnter=(e)=>{ if(e.keyCode===13){ needLists.push({ content:e.target.value, status:0 }) setNeedLists([...needLists]) e.target.value="" } }
對於對象的修改也是同理可以通過{…obj}
關於useState使用及註意事項
一、基本使用
useState是 react 提供的一個定義響應式變量的 hook 函數,基本語法如下:
const [count, setCount] = useState(initialCount)
它返回一個狀態和一個修改狀態的方法,狀態需要通過這個方法來進行修改;initialCount 是我們傳入的一個初始狀態,它是惰性的,我們可以通過傳一個函數來返回一個值當作初始狀態,並且這個函數隻會在初始渲染時執行一次;
const [count, setCount] = useState(() => { const initialCount = someExpensiveComputation(); return initialCount })
接下來把定義好的狀態運用到頁面:
import { useState } from 'react' function App() { const [count, setCount] = useState(0) const handleClick = () => { setCount(count + 1) // 傳入一個函數,更新的值是基於之前的值來執行 // setCount(count => count + 1) } return ( <div> <h4>count: {count}</h4> <button onClick={ handleClick }>點擊更新狀態</button> </div> ) }
頁面渲染完成後,我們可以看到 count的值是 0,當我們點擊按鈕時,會將 count的值加 1,頁面也同時更新;
瞭解完基礎用法後,我們可以思考幾個問題;
- setCount修改值時它是同步還是異步?
- 連續調用 setCount會發生什麼?
第一個問題:setCount修改值時它是同步還是異步?
const handleClick = () => { console.log("value1: ", count) setCount(count => count + 1) console.log("value2: ", count) }
從圖中我們可以看出,頁面的值是更新瞭,但是控制臺打印的是之前的值,這是不是也表示 setCount是異步的呢?我們換一種方法,用異步來修改狀態;
const handleClick = () => { console.log("value1: ", count) setTimeout(() => { setCount(count => count + 1) console.log("value2: ", count) }, 0) }
顯然,異步修改狀態跟同步修改狀態的結果是一致的,這也表明瞭 setCount 是異步更新的;那我們要怎麼拿到更新後的值呢,我們可以用另外一個 hook 函數 useRef,代碼如下:
function App() { const [count, setCount] = useState(0) const countRef = useRef(count) countRef.current = count const handleClick = () => { setCount(count => count + 1) console.log("value3: ", count) setTimeout(() => { console.log(countRef.current) }, 0) } return ( <div> <h4>count: {count}</h4> <button onClick={handleClick}>點擊更新狀態</button> </div> ) }
從圖中我們可以看出,我們已經拿到瞭更新之後的值,useRef不僅可以用於訪問 DOM 節點,也可以用來表示一個容器,current屬性可以保存任何值,而且useRef返回的對象會在整個生命周期內保持;
第二個問題:連續調用 setCount會發生什麼?
(1)傳入一個基於狀態的值
const handleClick = () => { console.log("value1: ", count) setCount(count + 1) console.log("value2: ", count) setCount(count + 1) console.log("value3: ", count) }
從圖片可以看出,如果我們傳入的是一個普通值,他隻會進行最後一次更新;
(2)傳入一個函數
const handleClick = () => { console.log("value1: ", count) setCount(count => count + 1) console.log("value2: ", count) setCount(count => count + 1) console.log("value3: ", count) }
可以看出,傳入一個函數的話,它會進行兩次賦值,因為它更新的值是基於之前的值來執行,所以在開發中推薦使用函數傳入的形式進行修改;
二、註意事項
1、復雜變量的修改
對於復雜類型的變量我們修改時需要重新定義,在原來數據的基礎上修改不會引起組件的重新渲染,因為 React 組件的更新機制隻進行淺對比,也就是更新某個復雜類型數據時隻要它的引用地址沒變,就不會重新渲染組件;舉個例子
function App() { const [arr, setArr] = useState([1]) const pushData = () => { arr.push(4) setArr(arr) } return ( <div> <h4>{arr.join("-")}</h4> <button onClick={pushData}>點擊添加數組</button> </div> ) }
上面的代碼在點擊按鈕時,視圖不會發生變化,但是 arr的值是變化瞭,如果想修改這個數組,需要重新定義一個數組來修改,在原數組上的修改不會引起組件的重新渲染,React 組件的更新機制對隻進行淺對比,也就是更新某個復雜類型數據時隻要它的引用地址沒變,就不會重新渲染組件;
const pushData = () => { setArr([...arr, 4]) }
2、異步操作獲取更新的值
在類組件裡面,修改值時異步操作可以拿到更新後的值,但是在函數組件,異步獲取是拿不到更新後的值的,舉個例子對比一下:
類組件
class App extends React.Component { constructor() { super() this.state = { count: 0 } } handleClick = () => { this.setState({ count: this.state.count + 1 }) console.log(this.state.count) setTimeout(() => { console.log(this.state.count) }) } render() { return ( <> <h4>count: {this.state.count}</h4> <button onClick={this.handleClick}>點擊更新狀態</button> </> ); } }
函數組件
function App() { const [count, setCount] = useState(0) const handleClick = () => { setCount(count => count + 1) console.log("value1: ", count) setTimeout(() => { console.log("value2: ", count) }) } return ( <div> <h4>count: {count}</h4> <button onClick={handleClick}>點擊更新狀態</button> </div> ) }
顯然,在函數組件中是不能通過異步來獲取更新的值,我們可以通過 useRef來獲取;
const countRef = useRef(count) countRef.current = count const handleClick = () => { setCount(count => count + 1) console.log("value1: ", countRef.current) setTimeout(() => { console.log("value2: ", countRef.current) }) }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- React hooks useState異步問題及解決
- React Hook用法示例詳解(6個常見hook)
- React中immutable的UI組件渲染性能詳解
- 如何解決React useEffect鉤子帶來的無限循環問題
- React-hooks面試考察知識點匯總小結(推薦)