官方介紹:Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。
注意事項:
- 只能在函數最外層調用Hook,不要再循環、條件判斷或者子函數中調用
- 只能在React的函數組件中調用Hook,不要在其他JS函數中調用
目錄
useState
- 通過在函數組件中調用它來給組件添加一些內部state;
- React會在重複渲染時保留這個state;
- useState唯一的參數就是初始state;
useState會返回一對值,當前狀態和一個讓你更新它的函數,你可以在事件處理函數中或者其他地方調用這個函數,它類似class組件中的this.setState,但是它不會把新的state和舊的state進行合併。
- 在初始渲染期間,返回的狀態(state)與傳入的第一個參數(initialState)值相同
- setState函數用於更新state,它接收一個新的state值並將組件的一次重新渲染加入隊列
特徵:
- 每次渲染都是獨立的閉包
- 每次渲染都有它自己的props and state
- 每次渲染都有它自己的事件處理函數
- alert會捕獲當前觸發時的狀態,不會再打印更新的state (原因:每次更新state會重新render組件)
function Counter(){
const [number,setNumber] = usestate(0);
const alertNumber = ()=>{
setTimeout(()=>{
alert(number)
},3000)
}
return (
<>
<p>{number}</p>
<button onClick={()=>setNumber(number+1)}>+</button>
<button onClick={alertNumber}>alert</button>
</>
)
}
ReactDOM.render(<Counter/>,document.getElementbyId('root'));
使用函數式更新可以解決問題
function Counter(){
const [number,setNumber] = usestate(0);
const alertNumber = ()=>{
setTimeout(()=>{
//如果這裏寫的函數,state就會一直以最新的狀態往上加
setNumber(number=>({number:number+1}));
alert(number);
},3000)
}
return (
<>
<p>{number}</p>
<button onClick={()=>setNumber(number+1)}>+</button>
<button onClick={alertNumber}>alert</button>
</>
)
}
ReactDOM.render(<Counter/>,document.getElementbyId('root'));
5. 惰性初始state
- initialState初始狀態參數只會有組件初始渲染的時候調用,後續渲染會被忽略
- 跟類組件setState不同,使用useState更新state不會自動合併,更新的時候需要傳入完整的值
let [state, setState] = useSate({number:0});
//....更新state時....
setState({...state,number:number+1})
useCallback性能優化
特性:更新useState時,如果state沒有變化組件不會刷新
爲了減少每次刷新state的渲染次數,我們可以使用useCallback,參數:fn和deps
- fn:執行的函數
- deps:依賴的變量,數組格式,只有在這個變量發生變化的時候纔會重新生成useCallback裏傳入的函數
function Counter(){
const [number,setNumber] = usestate(0);
const [name,setName] = usestate('tiya');
const addNumber = useCallback(setNumber(number+1),[name]);
const addName = useCallback(setName('bella'),[name]);
return (
<>
<p>{number}</p>
<p>{name}</p>
<button onClick={addNumber}>+</button>
<button onClick={()=>{
addName('bella')
}}>alert</button>
</>
)
}
ReactDOM.render(<Counter/>,document.getElementbyId('root'));
useMemo性能優化
參數:fn和deps
- fn:執行的函數,即官網(下文)說的--“創建”函數
- deps:依賴的變量,數組格式,只有在這個變量發生變化的時候纔會重新生成useMemo裏傳入的函數
注意:
把“創建”函數和依賴項數組作爲參數傳入
useMemo
,它僅會在某個依賴項改變時才重新計算 memoized 值。這種優化有助於避免在每次渲染時都進行高開銷的計算。記住,傳入
useMemo
的函數會在渲染期間執行。請不要在這個函數內部執行與渲染無關的操作,諸如副作用這類的操作屬於useEffect
的適用範疇,而不是useMemo
。如果沒有提供依賴項數組,
useMemo
在每次渲染時都會計算新的值。依賴項數組不會作爲參數傳給“創建”函數。未來也許有可能
// 案例
import React,{useState,useCallback,memo,useMemo} from 'react';
import ReactDOM from 'react-dom';
function Child(props){
console.log('render Child');
return (
<button onClick={props.addClick}>{props.data.number}</button>
)
}
Child = memo(Child);
function App() {
let [number,setNumber]=useState(0);
let [name,setName] = useState('tiya');
const addClick = useCallback(()=>setNumber(x=>x+1),[]);
//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 addClick={addClick} data={data}/>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);