乐趣区

关于css:你应该知道的Hooks知识

Hooks

HooksReact16.8 的新增个性,可能在不写 class 的状况下应用 state 以及其余个性。

动机

  • 在组件之间复用状态逻辑很难
  • 简单组件变得难以了解
  • 难以了解的 class

    Hooks 规定

  • 只有在最顶层应用 Hooks不要再循环 / 条件 / 嵌套函数中应用 `
  • 只有在 React 函数中调用 Hooks

    函数组件和类组件的不同

函数组件可能捕捉到以后渲染的所用的值。

点击查看示例

对于类组件来说,尽管 props是一个不可变的数据,然而 this是一个可变的数据,在咱们渲染组件的时候 this 产生了扭转,所以 this.props 产生了扭转,因而在 this.showMessage 中会拿到最新的 props 值。

对于函数组件来说捕捉了渲染所应用的值,当咱们应用 hooks 时,这种个性也同样的试用于 state 上。

点击查看示例

const showMessage = () => {alert("写入:" + message);
};

const handleSendClick = () => {setTimeout(showMessage, 3000);
};

const handleMessageChange = (e) => {setMessage(e.target.value);
};

如果咱们想跳出 ’ 函数组件捕捉以后渲染的所用值‘这个个性,咱们能够采纳 ref 来追踪某些数据。通 ref.current 能够获取到最新的值

const showMessage = () => {alert("写入:" + ref.current);
};

const handleSendClick = () => {setTimeout(showMessage, 3000);
};

const handleMessageChange = (e) => {setMessage(e.target.value);
    ref.current = e.target.value;
};

useEffect

useEffect 可能在函数组件中执行副作用操作(数据获取 / 波及订阅),其实能够把 useEffect 看作是 componentDidMount / componentDidUpdate / componentWillUnMount 的组合

  • 第一个参数是一个 callback,返回 destorydestory 作为下一个 callback 执行前调用,用于革除上一次 callback 产生的副作用
  • 第二个参数是依赖项,一个数组,能够有多个依赖项。依赖项扭转,执行上一个 callback 返回的 destory,和执行新的 effect 第一个参数 callback

对于 useEffect 的执行,React 解决逻辑是 采纳异步调用 的,对于每一个 effectcallback 会像 setTimeout 回调函数一样,放到工作队列外面,等到主线程执行结束才会执行。所以 effect 的回调函数不会阻塞浏览器绘制视图

  1. 相干的生命周期替换计划

    • componentDidMount 代替计划
    React.useEffect(()=>{// 申请数据,事件监听,操纵 DOM},[]) //dep=[],只有在初始化执行
    /* 
      因为 useEffect 会捕捉 props 和 state,所以即便是在回调函数中咱们拿到的还是最后的 props 和 state
    */
    • componentDidUnmount 代替计划
    React.useEffect(()=>{
    /* 申请数据,事件监听,操纵 dom,减少定时器,延时器 */
    return function componentWillUnmount(){/* 解除事件监听器,革除定时器,延时器 */}
    },[])/* 切记 dep = [] */
    
    //useEffect 第一个函数的返回值能够作为 componentWillUnmount 应用
    • componentWillReceiveProps 代替计划

    其实两者的执行机会是齐全不同的,一个在 render 阶段,一个在 commit 阶段,useEffect 会初始化执行一次,然而 componentWillReceiveProps 只会在 props 变动时执行更新

    React.useEffect(()=>{console.log('props 变动:componentWillReceiveProps')
    },[props])
    • componentDidUpdate 代替计划

    useEffectcomponentDidUpdate 在执行期间尽管有点差异,useEffect 是异步执行,componentDidUpdate 是同步执行,但都是在 commit 阶段

    React.useEffect(()=>{console.log('组件更新实现:componentDidUpdate')     
    }) // 没有 dep 依赖项,没有第二个参数,那么每一次执行函数组件,都会执行该 effect。
  2. useEffect 中 [] 须要解决什么

React 官网 FAQ 这样说:

只有当函数 (以及它所调用的函数) 不援用 propsstate 以及由它们衍生而来的值时,你能力释怀地把它们从依赖列表中省略,应用 eslint-plugin-react-hooks 帮忙咱们的代码做一个校验

点击查看具体示例

function Counter() {const [count, setCount] = useState(0);

  useEffect(() => {const id = setInterval(() => {setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return <h1>{count}</h1>;
}
// 只会做一次更新,而后定时器不再转动
  1. 是否应该把函数当做 effect 的依赖
const loadResourceCatalog = async () => {if (!templateType) return
    const reqApi = templateType === TEMPLATE_TYPE.STANDARD ? 'listCatalog' : 'getCodeManageCatalog'
    const res: any = await API[reqApi]()
    if (!res.success) return
    setCatalog(res.data)
}

useEffect(() => {loadResourceCatalog();
}, [])
// 在函数 loadResourceCatalog 中应用了 templateType 这样的一个 state
// 在开发的过程中可能会遗记函数 loadResourceCatalog 依赖 templateType 值

第一个简略的解法,对于 某些只在 useEffect 中应用的函数,间接定义在 effect 中,以至于可能间接依赖某些 state

useEffect(() => {const loadResourceCatalog = async () => {if (!templateType) return
        const reqApi = templateType === TEMPLATE_TYPE.STANDARD ? 'listCatalog' : 'getCodeManageCatalog'
        const res: any = await API[reqApi]()
        if (!res.success) return
        setCatalog(res.data)
    }
    loadResourceCatalog();}, [templateType])

如果咱们须要在很多中央用到咱们定义的函数,不可能把定义放到以后的 effect 中,并且将函数放到了第二个的依赖参数中,那这个代码将就进入死循环。因为函数在每一次渲染中都返回一个 新的援用

const Template = () => {const getStandardTemplateList = async () => {const res: any = await API.getStandardTemplateList()
      if (!res.success) return;
        const {data} = res;
        setCascaderOptions(data);
        getDefaultOption(data[0])
    }
    useEffect(()=>{getStandardTemplateList()
    }, [])
}


针对这种状况,如果以后函数没有援用任何组件内的任何值,能够将该函数提取到组件里面去定义,这样就不会组件每次 render 时不会再次扭转函数援用。

const getStandardTemplateList = async () => {const res: any = await API.getStandardTemplateList()
  if (!res.success) return;
    const {data} = res;
    setCascaderOptions(data);
    getDefaultOption(data[0])
}

const Template = () => {useEffect(()=>{getStandardTemplateList()
    }, [])
}

如果说以后函数中援用了组件内的一些状态值,能够采纳 useCallBack 对以后函数进行包裹

const loadResourceCatalog = useCallback(async () => {if (!templateType) return
    const reqApi = templateType === TEMPLATE_TYPE.STANDARD ? 'listCatalog' : 'getCodeManageCatalog'
    const res: any = await API[reqApi]()
    if (!res.success) return
    setCatalog(res.data)
}, [templateType])

useEffect(() => {loadResourceCatalog();
}, [loadResourceCatalog])
// 通过 useCallback 的包裹,如果 templateType 放弃不变,那么 loadResourceCatalog 也会放弃不变,所以 useEffect 也不会从新运行
// 如果 templateType 扭转,那么 loadResourceCatalog 也会扭转,所以 useEffect 也会从新运行

useCallback

React 官网定义

useCallback(fn, deps)

返回一个 memoized 回调函数,该回调函数仅在某个依赖项扭转时才会更新

import React, {useCallback, useState} from "react";

const CallBackTest = () => {const [count, setCount] = useState(0);
  const [total, setTotal] = useState(0);
  const handleCount = () => setCount(count + 1);
  //const handleCount = useCallback(() => setCount(count + 1), [count]);
  const handleTotal = () => setTotal(total + 1);

  return (
    <div>
      <div>Count is {count}</div>
      <div>Total is {total}</div>
      

      <div>
        <Child onClick={handleCount} label="Increment Count" />
        <Child onClick={handleTotal} label="Increment Total" />
      </div>
    </div>
  );
};

const Child = React.memo(({onClick, label}) => {console.log(`${label} Child Render`);
  return <button onClick={onClick}>{label}</button>;
});

export default CallBackTest;

点击查看具体示例

React.memo 是通过记忆组件渲染后果的形式来进步性能,memoreact16.6 引入的新属性,通过 浅比拟 (源码通过 Object.is 办法比拟) 以后依赖的 props 和下一个 props 是否雷同来决定是否从新渲染;如果应用过类组件形式,就能晓得 memo 其实就相当于 class 组件中的 React.PureComponent,区别就在于 memo 用于函数组件。useCallbackReact.memo 肯定要联合应用能力有成果。

应用场景

  • 作为 props,传递给子组件,为防止子元素不必要的渲染,须要配合 React.Memo 应用,否则无意义
  • 作为 useEffect 的依赖项,须要进行比拟的时候才须要加上 useCallback

    useMemo

React 官网定义

返回一个 memoized

仅会在某个依赖项扭转时才从新计算 memoized 值,这种优化有助于防止在每次渲染时都进行高开销的计算 useCallback(fn, deps) 相当于 useMemo(() => fn, deps),对于实现上,基本上是和 useCallback 类似,只是稍微有些不同

应用场景

  • 防止在每次渲染时都进行高开销的计算

两个 hooks 内置于 React 都有特地的起因:

1. 援用相等

当在 React 函数组件中定义一个对象时,它跟上次定义的雷同对象,援用是不一样的(即便它具备所有雷同值和雷同属性)

  • 依赖列表
  • React.memo

大多数时候,你不须要思考去优化不必要的从新渲染,因为优化总会带来老本。

  1. 低廉的计算
    计算成本很高的同步计算值的函数

总结

本文介绍了 hooks 产生动机、函数组件和类组件的区别以及 useEffect / useCallback / useMemo 等内容。重点介绍了 useEffect 的生命周期替换计划以及是否把函数作为 useEffect 的第二参数。

参考链接

When to useMemo and useCallback

How to fetch data with React Hooks

A Complete Guide to useEffect

How Are Function Components Different from Classes?

useCallback、useMemo 剖析 & 差异
网络的导航,是从输出 url 到最终获取到文件的过程。其中牵扯到浏览器架构、操作系统、网络等一系列常识。本文将从各个角度具体阐述这一过程,波及广度与深度。如果您是曾经有肯定根底的同学,那么本文能够疾速带你系统化整顿碎片化常识。

退出移动版