1、useReducer
適用情況為對一個狀態多種復雜操作,通俗的講就是比如對count這個變量加減乘除的各種情況
改造前
import { useState } from "react";function App() {//計數器const [count, setCount] = useState(0);const handleIncrement = () => {setCount(count + 1);};const handleDecrement = () => {setCount(count - 1);};return (<div style={{ padding: 10 }}><button onClick={handleIncrement}>+</button><span>{count}</span><button onClick={handleDecrement}>-</button></div>);
}export default App;
改造后
import { useReducer } from "react";
function countReducer(state, action) {switch (action.type) {case "increment":return state + 1;case "decrement":return state - 1;default:throw new Error();}
}
function App() {//計數器const [state, dispatch] = useReducer(countReducer, 0);const handleIncrement = () => {dispatch({ type: "increment" });};const handleDecrement = () => {dispatch({ type: "decrement" });};return (<div style={{ padding: 10 }}><button onClick={handleIncrement}>+</button><span>{state}</span><button onClick={handleDecrement}>-</button></div>);
}export default App;
2、useRef
ref記住狀態變更之前的值
import { useRef, useState } from "react";function App() {const [count, setCount] = useState(0);const prevCount = useRef();function handleClick() {prevCount.current = count;setCount(count + 1);}return (<div><p>當前的count:{count}</p><p>上一次的count:{prevCount.current}</p><button onClick={handleClick}>增大count</button></div>);
}export default App;
ref獲取標簽
import { useRef } from "react";
function App() {const inputRef = useRef(null);function handleClick() {inputRef.current.focus();}return (<div><input type="text" ref={inputRef} /><button onClick={handleClick}>按鈕</button></div>);
}export default App;
ref獲取其他子組件
需要以下步驟
1、子組件不能定義為一個普通函數,必須使用函數表達式的方式
2、這個函數表達式需要forwardRef包裹處理,這樣才可以被父組件使用
3、父組件給子組件傳入ref
4、子組件中的方法還需要useImperativeHandle包裹處理,才能被父組件調用
import { useRef, forwardRef, useImperativeHandle } from "react";
const Child = forwardRef(function (props, ref) {useImperativeHandle(ref, () => ({//暴露給父組件的方法myFn: () => {console.log("子組件myFn的方法");},}));return <div>子組件</div>;
});
function App() {const childRef = useRef();function handleClick() {childRef.current.myFn();}return (<div><Child ref={childRef} /><button onClick={handleClick}>點擊</button></div>);
}export default App;
3、useEffect
React要求所有的函數式組件都是純函數,意味同樣的輸入就有同樣的輸出
如果想要設置副作用的話,那我們可以使用useState事件這種
如果想要在組件加載或者組件更新時(非用戶操作觸發),能夠執行一些副作用的話,需要用到useEffect
useEffect在React嚴格模式下默認會執行兩次
如果想要在組件渲染的時候執行一次,以后就不再變更的話,可以給useEffect傳遞一個空的依賴數組,如果數組中填寫了一些狀態的話,這些狀態的變化會導致副作用的重新執行
import { useEffect, useState } from "react";
function App() {const [count, setCount] = useState(0);const handleIncrement = () => {setCount(count + 1);};const handleDecrement = () => {setCount(count - 1);};useEffect(() => {console.log("useEffect");}, [count]);return (<div style={{ padding: 10 }}><button onClick={handleIncrement}>+</button><span>{count}</span><button onClick={handleDecrement}>-</button></div>);
}export default App;
4、useMemo
一種用來緩存數據的鉤子
以下代碼,父組件改變count的值時,父組件會重新渲染,子組件也會隨著父組件渲染,但是子組件并沒有狀態變更,不需要重新渲染,這沒有意義,所以需要使用useMemo包裹處理需要緩存的那個數據,來監聽那個數據有沒有變化,使得子組件沒有狀態變更就不重新渲染,提高性能
import { useState } from "react";
function DoSomeMath({ value }) {console.log("DoSomeMath執行了");let result = 0;for (let i = 0; i < 1000000; i++) {result += value * 2;}return (<div><p>輸入內容:{value}</p><p>經過復雜計算的數據:{result}</p></div>);
}
function App() {const [inputValue, setInputValue] = useState(5);const [count, setCount] = useState(0);return (<div><p>count的值為:{count}</p><button onClick={() => setCount(count + 1)}>點擊更新</button><br /><br /><inputtype="number"value={inputValue}onChange={(e) => setInputValue(parseInt(e.target.value))}/><DoSomeMath value={inputValue} /></div>);
}export default App;
優化后的代碼
import { useState } from "react";
import { useMemo } from "react";
function DoSomeMath({ value }) {const result = useMemo(() => {console.log("DoSomeMath執行了");let result = 0;for (let i = 0; i < 1000000; i++) {result += value * 2;}return result;}, [value]);return (<div><p>輸入內容:{value}</p><p>經過復雜計算的數據:{result}</p></div>);
}
function App() {const [inputValue, setInputValue] = useState(5);const [count, setCount] = useState(0);return (<div><p>count的值為:{count}</p><button onClick={() => setCount(count + 1)}>點擊更新</button><br /><br /><inputtype="number"value={inputValue}onChange={(e) => setInputValue(parseInt(e.target.value))}/><DoSomeMath value={inputValue} /></div>);
}export default App;
5、useCallback
一種用來緩存函數的鉤子
以下代碼當父組件更新count狀態時,父組件會重新渲染,所以handleClick會重新創建變成一個新的函數,那么傳給子組件的handleClick也會變成新的props,但實際上父組件傳給子組件的props沒有變化,但是子組件會重新渲染
要解決這個問題,有兩個步驟要做:
a、將要緩存的函數使用memo記憶體包裹,并且寫成函數表達式的形式,變成記憶組件。memo的作用是當傳入的prop是同一個沒有變化時,就不會讓子組件重新渲染
b、使用useCallback包裹父組件的那個傳入子組件的函數,使得父組件重新渲染時,因為緩存了該函數,不會重新創建該函數,使得組件傳入的props沒變,最終使得子組件不會被重新渲染
?
import { useState } from "react";
function Button({ onClick }) {console.log("Button渲染了");return <button onClick={onClick}>子組件</button>;
}
function App() {const [count, setCount] = useState(0);const handleClick = () => {console.log("點擊按鈕");};const handleUpdate = () => {setCount(count + 1);};return (<div><p>Count:{count}</p><button onClick={handleUpdate}>點擊</button><br /><Button onClick={handleClick}></Button></div>);
}export default App;
優化后的代碼
import { memo, useCallback, useState } from "react";
const Button = memo(function ({ onClick }) {console.log("Button渲染了");return <button onClick={onClick}>子組件</button>;
});
function App() {const [count, setCount] = useState(0);const handleClick = useCallback(() => {console.log("點擊按鈕");}, []); //依賴項為空數組,表示該函數只在組件掛載時創建一次const handleUpdate = () => {setCount(count + 1);};return (<div><p>Count:{count}</p><button onClick={handleUpdate}>點擊</button><br /><Button onClick={handleClick}></Button></div>);
}export default App;