React函數式組件值之useState()


  react hooks 是 React 16.8 的新增特性。 它可以讓我們在函數組件中使用 state 、生命周期以及其他 react 特性,而不僅限於 class 組件。react hooks 的出現,標示着 react 中不會在存在無狀態組件了,只有類組件和函數組件。

  狀態是隱藏在組件中的信息,組件可以在父組件不知道的情況下修改其狀態。相比類組件,函數組件足夠簡單,要使函數組件具有狀態管理,可以useState() Hook。

一、基礎用法

  定義一個state,以及更新 state 的函數。在初始渲染期間,返回的狀態 (state) 與傳入的第一個參數 (initialState) 值相同。setState 函數用於更新 state。它接收一個新的 state 值並將組件的一次重新渲染加入隊列。在后續的重新渲染中,useState 返回的第一個值將始終是更新后最新的 state。

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

例子:

1   function App () { 2     const [ count, setCount ] = useState(0) 3     return ( 4       <div>
5  點擊次數: { count } 6         <button onClick={() => { setCount(count + 1)}}>點我</button>
7       </div>
8  ) 9   }

  當我們在使用 useState 時,修改值時傳入同樣的值,組件不會重新渲染,這點和類組件setState保持一致。

二、初始值與初始函數

  useState 支持我們在調用的時候直接傳入一個值,來指定 state 的默認值,比如這樣 useState(0), useState({ a: 1 }), useState([ 1, 2 ]),還支持我們傳入一個函數,來通過邏輯計算出默認值,比如這樣

 1 function App (props) {  2     const [ count, setCount ] = useState(() => {  3       return props.count || 0
 4  })  5     return (  6       <div>
 7  點擊次數: { count }  8         <button onClick={() => { setCount(count + 1)}}>點我</button>
 9       </div>
10  ) 11   }

  useState 中的函數只會在初始化的時候執行一次。

三、函數式更新

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

 1 function Counter() {  2   const [count, setCount] = useState(0);  3   function handleClick() {  4     setCount(count + 1)  5  }  6   function handleClickFn() {  7     setCount((prevCount) => {  8       return prevCount + 1
 9  }) 10  } 11   return ( 12     <>
13  Count: {count} 14       <button onClick={handleClick}>+</button>
15       <button onClick={handleClickFn}>+</button>
16     </>
17  ); 18 }

  handleClick和handleClickFn一個是通過一個新的 state 值更新,一個是通過函數式更新返回新的 state。現在這兩種寫法沒有任何區別,但是如果是異步更新的話,區別就顯現出來了。

 1 function Counter() {  2   const [count, setCount] = useState(0);  3   function handleClick() {  4     setTimeout(() => {  5       setCount(count + 1)  6     }, 3000);  7  }  8   function handleClickFn() {  9     setTimeout(() => { 10       setCount((prevCount) => { 11         return prevCount + 1
12  }) 13     }, 3000); 14  } 15   return ( 16     <>
17  Count: {count} 18       <button onClick={handleClick}>+</button>
19       <button onClick={handleClickFn}>+</button>
20     </>
21  ); 22 }

  當設置為異步更新,點擊按鈕延遲到3s之后去調用setCount函數,當快速點擊按鈕時,也就是說在3s多次去觸發更新,但是只有一次生效,因為 count 的值是沒有變化的。而當使用函數式更新 state 的時候,這種問題就沒有了,因為它可以獲取之前的 state 值,也就是代碼中的 prevCount 每次都是最新的值。

  其實這個特點和類組件中 setState 類似,可以接收一個新的 state 值更新,也可以函數式更新。如果新的 state 需要通過使用先前的 state 計算得出,那么就要使用函數式更新。因為setState更新可能是異步,當你在事件綁定中操作 state 的時候,setState更新就是異步的。

  一般操作state,因為涉及到 state 的狀態合並,react 認為當你在事件綁定中操作 state 是非常頻繁的,所以為了節約性能 react 會把多次 setState 進行合並為一次,最后在一次性的更新 state,而定時器里面操作 state 是不會把多次合並為一次更新的。

四、使用優化

  在 React 應用中,當某個組件的狀態發生變化時,它會以該組件為根,重新渲染整個組件子樹。

 1 function Child({ onButtonClick, data }) {  2   console.log('Child Render')  3   return (  4     <button onClick={onButtonClick}>{data.number}</button>
 5  )  6 }  7 
 8 function App() {  9   const [number, setNumber] = useState(0) 10   const [name, setName] = useState('hello') // 表單的值
11   const addClick = () => setNumber(number + 1) 12   const data = { number } 13   return ( 14     <div>
15       <input type="text" value={name} onChange={e => setName(e.target.value)} />
16       <Child onButtonClick={addClick} data={data} />
17     </div>
18  ) 19 }

  上述代碼中,子組件引用了number相關數據,但是當name相關數據發生變化,也會重繪整個組件,子組件雖然沒有任何變化,也會重繪。為了避免不必要的子組件的重渲染,需要使用useMemo和useCallback的Hook。

 1 function Child({ onButtonClick, data }) {  2   console.log('Child Render')  3   return (  4     <button onClick={onButtonClick}>{data.number}</button>
 5  )  6 }  7 
 8 Child = memo(Child)  9 
10 function App() { 11   const [number, setNumber] = useState(0) 12   const [name, setName] = useState('hello') // 表單的值
13   const addClick = useCallback(() => setNumber(number + 1), [number]) 14   const data = useMemo(() => ({ number }), [number]) 15   return ( 16     <div>
17       <input type="text" value={name} onChange={e => setName(e.target.value)} />
18       <Child onButtonClick={addClick} data={data} />
19     </div>
20  ) 21 } 22 
23 export default App;

  把“創建”函數和依賴項數組作為參數傳入 useMemo,它僅會在某個依賴項改變時才重新計算 memoized 值。這種優化有助於避免在每次渲染時都進行高開銷的計算。如果沒有提供依賴項數組,useMemo 在每次渲染時都會計算新的值。

  useCallback返回一個 memoized 回調函數。useCallback(fn, deps) 相當於 useMemo(() => fn, deps)。

  useCallback 和 useMemo 參數相同,第一個參數是函數,第二個參數是依賴項的數組。主要區別是 React.useMemo 將調用 fn 函數並返回其結果,而 React.useCallback 將返回 fn 函數而不調用它。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM