多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# react hook 组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来 * useState【维护状态】 * useEffect【完成副作用操作】 * useContext【使用共享状态】 * useReducer【类似redux】 * useCallback【缓存函数】 * useMemo【缓存值】 * useRef【访问DOM】 * useImperativeHandle【使用子组件暴露的值/方法】 * useLayoutEffect【完成副作用操作,会阻塞浏览器绘制】 ## **useState()** `const [name, setName] = useState(initState) ` 使用 `useState` 定义 state 变量时候,返回一个有两个值的数组。第一个值是当前的 state,第二个值是更新 state 的函数。 ```js 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 ``` ## **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写法不变。 ```js 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></> ); }; ``` ## **useReducer** ## **useMemo -- 缓存值** ```js const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]) // 不管页面 render 几次,时间戳都不会被改变,因为已经被被缓存了,除非依赖改变 const getNumUseMemo = useMemo(() => `${+new Date()}`, []) ``` 在a和b的变量值不变的情况下,memoizedValue的值不变,useMemo函数的第一个入参函数不会被执行,节省计算量(像vue的计算属性) ```js 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 ``` 在父组件中Input输入框每次输入新的值,父组件的info的值就会发生改变,子组件每次都会重新渲染,即使子组件没用到info值,因为setInfo导致父组件重新渲染了,也导致onVisible每次都变成一个新的值,所以引起子组件重新渲染。 利用React.memo,props.onVisible是一个函数,它是一个引用类型的值,当父组件重新渲染onVisible 这个函数也会重新生成,这样引用地址变化就导致对比出新的数据,子组件就会重新渲染。 ## **useCallback -- 缓存函数** ```js // 除非 `a` 或 `b` 改变,否则不会变 const memoizedCallback = useCallback(() => doSomething(a, b), [a, b],); ``` react中只要父组件的 render 了,那么默认情况下就会触发子组的render,避免这种重渲染方法: `React.PureComponent`、`React.memo` ,`shouldComponentUpdate()` useMemo是缓存值,useCallback一个是缓存函数的引用。也就是说 useCallback(fn, [deps]) 相当于 useMemo(() => fn, [deps]) ```js 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 ``` ## useRef - 判断是否是由于页面更新而非首次渲染 - 获取 Dom 元素,在函数组件中通过useRef 来获取对应的 Dom 元素,拿到子组件的实例,相当于class组件的`React.createRef()` ```js import { useRef } from 'react'; export function useFirstMountState(): boolean { const isFirst = useRef(true); if (isFirst.current) { isFirst.current = false; return true; } return isFirst.current; } ``` 访问DOM,从而操作DOM,如点击按钮聚焦文本框 ```js const Index = () => { const inputEl = useRef(null); const handleFocus = () => { inputEl.current.focus(); }; return (<> <input ref={inputEl} type="text" /> <button onClick={handleFocus}>Focus</button> </>); }; ``` 要访问的是一个组件,操作组件里的具体DOM----React.forwardRef 高阶组件来转发ref ```js 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} />; }); ``` # **自定义 hook**