官方介绍: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'));
//useState是useReducer的语法糖
function useState(initalState){
const reducer = useCallback((state,action)=>action);
let [state,dispatch] = useReducer(reducer,initialState);
function setState({payload}){
dispatch({payload});
};
return [state,setState];
}
使用函数式更新可以解决问题
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')
);
useReducer
function reducer(state,action){
switch(action.type){
case 'Add':
return {number:state.number+1};
case 'Minus':
return {number:state.number-1};
default:
return state;
}
}
const App = ()=>{
let [state,dispatch] = useReducer(reducer,initalState);
return (
<div>
<p>{state.number}</p>
<button onClick={()=>dispacth({type:'Add'})}>+</button>
<button onClick={()=>dispacth({type:'Minus'})}>-</button>
</div>
)
}