react的hook总结
# react hook
组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来
- useState【维护状态】
- useEffect【完成副作用操作】
- useContext【使用共享状态】
- useReducer【类似redux】
- useCallback【缓存函数】
- useMemo【缓存值】
- useRef【访问DOM】
- useImperativeHandle【使用子组件暴露的值/方法】
- useLayoutEffect【完成副作用操作,会阻塞浏览器绘制】
# useState()
const [name, setName] = useState(initState)
使用 useState
定义 state 变量时候,返回一个有两个值的数组。第一个值是当前的 state,第二个值是更新 state 的函数。
import React, { useState } from 'react'
import { Button } from 'antd'
const Home: React.FC<Iprops> = ({ dispatch, goodsList }) => {
const [info, setInfo] = useState('init info')
return (
<div>
<p>{info}</p>
<Button onClick={() => setinfo('改变info')}> 点击更改info</Button>
</div>
)
}
export default Home
2
3
4
5
6
7
8
9
10
11
12
# useEffect -- 副作用钩子
useEffet 合成了calss组件中的componentDidMount, componentDidUpdate, componentWillUnmount 这三个生命周期
# useContext():共享状态钩子
Context :在组件之间共享值,不必显式地通过组件树的逐层传递 props。类似于 React 中Context Api 和 Vue 中的 provide/inject Api
使用context的子组件直接使用React.useContext(Context)
就可获得context,而在Context Api中需使用<Consumer>({vlaue} => {})</Consumer>
;父组件Provider写法不变。
const obj = { value: 1 };
const obj2 = { value: 2 };
const ObjContext = React.createContext(obj);
const Obj2Context = React.createContext(obj2);
const App = () => { return (
<ObjContext.Provider value={obj}>
<Obj2Context.Provider value={obj2}> <ChildComp /> </Obj2Context.Provider>
</ObjContext.Provider>
);};
// 子级
const ChildComp = () => { return <ChildChildComp />; };
// 孙级或更多级
const ChildChildComp = () => {
const obj = useContext(ObjContext);
const obj2 = useContext(Obj2Context);
return ( <><div>{obj.value}</div><div>{obj2.value}</div></> );
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# useReducer
# useMemo -- 缓存值
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
// 不管页面 render 几次,时间戳都不会被改变,因为已经被被缓存了,除非依赖改变
const getNumUseMemo = useMemo(() => `${+new Date()}`, [])
2
3
在a和b的变量值不变的情况下,memoizedValue的值不变,useMemo函数的第一个入参函数不会被执行,节省计算量(像vue的计算属性)
import React, { useState } from 'react'
import { Input } from 'antd'
import Son1 from './son1'
interface Iprops {}
const Home: React.FC<Iprops> = () => {
const [info, setInfo] = useState('')
const [visible, setVisible] = useState(true)
// const onVisible = () => { setVisible((visible) => !visible) }
const onVisible = useMemo(() => {
return () => { setVisible((visible) => !visible) }
}, [])
const changeInfo = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
setInfo(value)
}
return ( <div>
<p>{info}</p>
<Input onChange={(e) => changeInfo(e)}></Input>
<Son1 onVisible={onVisible} />
</div>)
}
export default Home
// 子组件
import React, { memo } from 'react'
import { Button } from 'antd'
interface Iprops { onVisible: () => void }
const Son1: React.FC<Iprops> = ({ onVisible }) => {
console.log('我被重新渲染了....')
return ( <Button onClick={() => onVisible()}>button</Button> )
}
// export default memo(Son1)
export default Son1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
在父组件中Input输入框每次输入新的值,父组件的info的值就会发生改变,子组件每次都会重新渲染,即使子组件没用到info值,因为setInfo导致父组件重新渲染了,也导致onVisible每次都变成一个新的值,所以引起子组件重新渲染。
利用React.memo,props.onVisible是一个函数,它是一个引用类型的值,当父组件重新渲染onVisible 这个函数也会重新生成,这样引用地址变化就导致对比出新的数据,子组件就会重新渲染。
# useCallback -- 缓存函数
// 除非 `a` 或 `b` 改变,否则不会变
const memoizedCallback = useCallback(() => doSomething(a, b), [a, b],);
2
react中只要父组件的 render 了,那么默认情况下就会触发子组的render,避免这种重渲染方法: React.PureComponent
、React.memo
,shouldComponentUpdate()
useMemo是缓存值,useCallback一个是缓存函数的引用。也就是说 useCallback(fn, [deps]) 相当于 useMemo(() => fn, [deps])
const Home: React.FC<Iprops> = () => {
const [info, setInfo] = useState('')
const [visible, setVisible] = useState(true)
// const onVisible = useMemo(() => { return () => setVisible((visible) => !visible) }, [])
const onVisible = useCallback(() => { setVisible(visible => !visible)}, [])
const changeInfo = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
setInfo(value)
}
return (<div>
<p>{info}</p>
<Input onChange={(e) => changeInfo(e)}></Input>
<Son1 onVisible={onVisible} />
</div>)
}
export default Home
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# useRef
- 判断是否是由于页面更新而非首次渲染
- 获取 Dom 元素,在函数组件中通过useRef 来获取对应的 Dom 元素,拿到子组件的实例,相当于class组件的
React.createRef()
import { useRef } from 'react';
export function useFirstMountState(): boolean {
const isFirst = useRef(true);
if (isFirst.current) {
isFirst.current = false;
return true;
}
return isFirst.current;
}
2
3
4
5
6
7
8
9
访问DOM,从而操作DOM,如点击按钮聚焦文本框
const Index = () => {
const inputEl = useRef(null);
const handleFocus = () => { inputEl.current.focus(); };
return (<>
<input ref={inputEl} type="text" />
<button onClick={handleFocus}>Focus</button>
</>);
};
2
3
4
5
6
7
8
要访问的是一个组件,操作组件里的具体DOM----React.forwardRef 高阶组件来转发ref
const Index = () => {
const inputEl = useRef(null);
const handleFocus = () => { inputEl.current.focus(); };
return ( <>
<Child ref={inputEl} />
<button onClick={handleFocus}>Focus</button>
</>);
};
const Child = forwardRef((props, ref) => {
return <input ref={ref} />;
});
2
3
4
5
6
7
8
9
10
11