什么是react-hooks

react-hooks是react16.8以后,react新增的钩子API,目的是增加代码的可复用性,逻辑性,弥补无状态组件没有生命周期,没有数据管理状态state的缺陷。笔者认为,react-hooks思想和初衷,也是把组件,颗粒化,单元化,形成独立的渲染环境,减少渲染次数,优化性能。

useState

使用状态

usestate 返回一个数组,第一个参数用来读取,第二个参数用来写

useState的参数可以是一个具体的值,也可以是一个函数用于判断复杂的逻辑,函数返回作为初始值

  1. const [n , setN]=React.useState(0) // 初始值
  2. const [user ,setUser] = React.useState({name:'F'})

useState不能局部更新,里面的参数必须全部更新

setState不会帮我们合并属性,合并可采取剩余操作符

  1. setUser({
  2. ...user,
  3. name: 'Jack'
  4. })

setUser必须是一个新的对象,不会是同一个地址

useState写成函数的优点,减少多余的计算过程

  1. const [state, setState] = useState(()=>{
  2. return initialState
  3. })

useState派发更新函数的执行,就会让整个function组件从头到尾执行一次,所以需要配合useMemo,usecallback等api配合使用。

当我们在调用更新函数之后,state的值是不能即时改变的,只有当下一次上下文执行的时候,state值才随之改变。

useReducer

用法

  • 看代码,共分4步走
  • 创建初始值initialState
  1. const initial = {
  2. n: 0
  3. };
  • 创建所有操作reducer(state, action)
  1. const reducer = (state, action) => {
  2. if (action.type === "add") {
  3. return { n: state.n + action.number };
  4. } else if (action.type === "multi") {
  5. return { n: state.n * 2 };
  6. } else {
  7. throw new Error("unknown type");
  8. }
  9. };
  • 传给useReducer,得到读和写API
  1. const [state, dispatch] = useReducer(reducer, initial);
  • 调用写({type:操作类型’})
  1. const onClick = () => {
  2. dispatch({ type: "add", number: 1 });
  3. };

useContext

使用useContext ,来获取父级组件传递过来的context值,这个当前值就是最近的父级组件 Provider 设置的value值,useContext参数一般是由 createContext 方式引入 。

父级上下文context传递 ,给个读写接口给整个页面用

使用方法

  • 使用C = createContext(initial)创建上下文
  1. const C = createContext(null);
  • 使用圈定作用域
  1. <C.Provider value={{ n, setN }}>
  2. <div className="App">
  3. <Baba />
  4. </div>
  5. </C.Provider>
  • 在作用域内使用useContext(C)来使用上下文
  1. const { n, setN } = useContext(C);

它不是响应式的,而是重新渲染的过程。

useEffect(afterRender)

useEffect 在浏览器渲染完成后执行

useEffect 执行顺序 组件更新挂载完成 -> 浏览器dom 绘制完成 -> 执行useEffect回调 。

可以当做componentDidMount和componentWillUnmount生命钩子使用。

用途

  • 作为componentDidMount使用,[] 作第二个参数
  1. useEffect(()=>{
  2. console.og('第一次渲染后执行这句话')
  3. },[])
  • 作为componentDidUpdate使用,可指定依赖
  1. useEffect(()=>{
  2. console.log('n变化了')
  3. },[n])
  4. useEffect(()=>{
  5. console.log('任何变化都执行')
  6. })
  • 作为componentWillUnmount使用,通过return
  1. useEffect(()=>{
  2. const id = setInterval(()=>{
  3. console.log('hi')
  4. },1000)
  5. return ()=>{
  6. window.clearInterval(id)
  7. }
  8. },[])

当我们对环境做了任何变动,我们都要清楚变动,否则会造成内存泄漏。

  • 以上三种用途可同时存在,同时存在时按照出现的顺序执行

useLayoutEffect

useLayoutEffect 在浏览器渲染前执行

useLayoutEffect 执行顺序 组件更新挂载完成 -> 执行useLayoutEffect回调-> 浏览器dom 绘制完成

如果这样使用多少会影响用户体验,延长用户看到画面的时间,优先使用useEffect

useL ayoutEffect总是比useEffect先执行

useL ayoutEffect里的任务最好影响了Layout

useMemo

可以实现函数的重用,用来缓存一些希望在2次新旧组件迭代的时候,用上一次的值。

useMemo可以减少不必要的循环,减少不必要的渲染

React.memo

React 默认是有多余的 render 。如果不想去渲染时,可以使用 React.memo代替

  1. const Child =React.memo(props =>{
  2. console.log('这里的代码不在被渲染')
  3. })

如果props不变,就没有必要再次执行一个函数组件

特点

第一个参数是()=> value,见定义

第二个参数是依赖[m,n]

只有当依赖变化时,才会计算出新的value

如果依赖不变,那么就重用之前的value

如果value 是一个函数

  1. useMemo(()=>{
  2. (x)=>{
  3. console.log(x)
  4. }
  5. },[m])

这样写有一个语法糖 直接上你想return的函数

  1. useCallback( (x)=>{
  2. console.log(x)
  3. },[m] )

useRef

目的

我们知道usestate ,useReducer 是可以保存当前的数据源的,但是如果它们更新数据源的函数执行必定会带来整个组件从新执行到渲染,如果你需要一个值,在组件不断render时保持不变,也就是缓存数据功能,useRef是一个不错的选择。

初始化: const count = useRef(0)

读取:count.current

为什么需要current?

为了保证两次useRef是同一个值(只有引用能做到)

React.useRef不会更新UI,要手动render

forwardRef

如果一个函数组建想要接受别人传来的ref,必须用forwardRef把自己包起来

  1. function App() {
  2. const buttonRef = useRef(null);
  3. return (
  4. <div className="App">
  5. <Button3 ref={buttonRef}>按钮</Button3>
  6. </div>
  7. );
  8. }
  9. const Button3 = React.forwardRef((props, ref) => {
  10. return <button className="red" ref={ref} {...props} />;
  11. });

useImperativeHandle

对 ref 进行设置。

极少使用

自定义Hook

自定义hooks是在react-hooks基础上的一个拓展,可以根据业务需要制定满足业务需要的hooks,更注重的是逻辑单元。通过业务场景不同,我们到底需要react-hooks做什么,怎么样把一段逻辑封装起来,做到复用,这是自定义hooks产生的初衷。

案例