React常用hook
1. useState
前端应用核心
只能写一些同步的计算逻辑
const [num, setNum] = useState()
2. useEffect
副作用
useEffect(() => {
//操作
}, [...])
//[]为依赖, 当[]内的元素改变时, 就会执行useEffect, 若[] 为空, 则只在挂在与销毁时执行
//而且useEffect中可以执行一些异步任务,但需要使用async, await单独写一个函数
3. useLasyoutEffect
与useEffect类似
但区别在执行的时机
js的任务会在渲染的间隔中执行
异步执行useEffect中的副作用effect会在渲染结束后执行, 因此可能会出现页面的闪动, 因第一次渲染之后是旧值,effect更改了值,在渲染变为新值。
而useLayoutEffect中的effect的执行是同步的, 因此会在渲染前执行完成
但要注意effect可能会执行很久, 使用layout可能会阻塞渲染
4. useReducer
当使用useState之前想要执行一些固定的逻辑
interface Data {
result: number;
}
interface Action {
type: 'add' | 'minus',
num: number
}
//要执行的逻辑
function reducer(state: Data, action: Action) {
switch(action.type) {
case 'add':
return {
result: state.result + action.num
}
case 'minus':
return {
result: state.result - action.num
}
}
return state;
}
function App() {
//Reducer的参数<数据的类型, action的类型>
const [res, dispatch] = useReducer<Reducer<Data, Action>>(reducer, { result: 0 });
return (
<div>
<div onClick={() => dispatch({ type: 'add', num: 2 })}>加</div>
<div onClick={() => dispatch({ type: 'minus', num: 1 })}>减</div>
<div>{res.result}</div>
</div>
);
}
可以传入第二个参数
const [res, dispatch] = useReducer<Reducer<Data, Action>, string >(reducer, 'zero', (param) => {
return {
result: param === 'zero' ? 0 : 1
}
});
5. immer
操作对象结构比较复杂的时候使用
interface Data {
a: {
c: {
e: number,
f: number
},
d: number
},
b: number
}
此时我们想要修改某个值
function reducer(state: Data, action: Action) {
switch(action.type) {
case 'add':
return {
...state,
a: {
...state.a,
c: {
...state.a.c,
e: state.a.c.e + action.num,
},
},
}
}
return state;
}
//我们需要再把整个修改后的对象返回, 因为react的State不可变性
5.1 如何使用immer
//安装
npm install --save immer
//使用
return produce(state, (state) => {
state.a.c.e += 100
})
//注意需要返回对象
6. useRef
保存 dom 引用
function App() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
inputRef.current?.focus();
});
return (
<div>
<input ref={inputRef}></input>
</div>
);
}
useRef 一般是用来存一些不是用于渲染的内容的。
7. forwardRef
把 ref 从子组件传递到父组件
const Guang: React.ForwardRefRenderFunction<HTMLInputElement> = (props, ref) => {
return <div>
<input ref={ref}></input>
</div>
}
const WrapedGuang = React.forwardRef(Guang);
function App() {
const ref = useRef<HTMLInputElement>(null);
useEffect(()=> {
console.log('ref', ref.current)
ref.current?.focus()
}, []);
return (
<div className="App">
<WrapedGuang ref={ref}/>
</div>
);
}
export default App;
当想要暴露一些自定义内容时, 使用useImperativeHandle
const Guang: React.ForwardRefRenderFunction<RefProps> = (props, ref) => {
const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => {
return {
aaa() {
inputRef.current?.focus();
}
}
}, [inputRef]);
return <div>
<input ref={inputRef}></input>
</div>
}
const WrapedGuang = React.forwardRef(Guang);
function App() {
const ref = useRef<RefProps>(null);
useEffect(()=> {
console.log('ref', ref.current)
ref.current?.aaa();
}, []);
return (
<div className="App">
<WrapedGuang ref={ref}/>
</div>
);
}
8. useContext
跨任意层组件传递数据
const countContext = createContext(111);
function Aaa() {
return <div>
//这里使用provider
<countContext.Provider value={222}>
<Bbb></Bbb>
</countContext.Provider>
</div>
}
function Bbb() {
return <div><Ccc></Ccc></div>
}
function Ccc() {
//这里获取
const count = useContext(countContext);
return <h2>context 的值为:{count}</h2>
}
用 createContext 创建 context 对象,用 Provider 修改其中的值, function 组件使用 useContext 的 hook 来取值,class 组件使用 Consumer 来取值。
9. memo
只有当props变化时才会重新渲染被包裹的组件
function Aaa() {
const [count, setCount] = useState(2);
useEffect(() => {
setTimeout(()=> {
setCount(Math.random());
}, 2000)
},[]);
return <div>
<MemoBbb count={2}></MemoBbb>
</div>
}
interface BbbProps {
count: number;
}
function Bbb(props: BbbProps) {
console.log('bbb render');
return <h2>{props.count}</h2>
}
const MemoBbb = memo(Bbb);
9.1 useMemo与useCallback
防止props不必要的更新
const bbbCallback = useCallback(function () {
// xxx
}, []);
当[]不变时,始终返回同一个function
useMemo保存值
const count2 = useMemo(() => {
return count * 10;
}, [count]);
如果子组件用了 memo,那给它传递的对象、函数类的 props 就需要用 useMemo、useCallback 包裹,否则,每次 props 都会变,memo 就没用了。
反之,如果 props 使用 useMemo、useCallback,但是子组件没有被 memo 包裹,那也没意义,因为不管 props 变没变都会重新渲染,只是做了无用功。
也可以用 useMemo 来缓存计算量比较大的。