如何在React中使用useCallback优化代码

React中父组件更新,子组件该如何?

首先看一段代码,对于父组件更新状态的时候,子组件是否会更新:

function Child(props){
    
    
  console.log('child更新')
  const {
    
    childName} = props
  return (
    <div>
      <h5>{
    
    childName}</h5>
      <button>changeChildName</button>
    </div>
  )
}

function Parent(){
    
    
  const [name,setName] = useState('farther')
  const [childName,setChildName] = useState('child')
  return (
    <div>
      <Child childName={
    
    childName} />
      <h4>{
    
    name}</h4>
      //更新name,父组件自己的状态
      <button onClick={
    
    () => {
    
    setName(Math.random())}}>changeFartherName</button>
    </div>
  )
}

在这个例子中,子组件的childName是从父组件的props中拿到的。但是当我点击按钮,修改的是父组件的状态,props并没有改变。
但是,子组件依然会更新。这是React更新的机制。但是在这种情况下,子组件是没有必要重新render一遍的。

基于上面的情况,memo方法出现了。

React.memo

memo是一个Hoc高阶组件,他可以对组件进行封装,而封装后的组件只有在props变化的时候才会render。
针对于上面的代码,就可以通过memo将子组件进行封装:

 function getChild(props) {
    
    
  //父组件更新自己的状态时,不会render
  console.log("child更新");
  const {
    
     childName } = props;
  return (
    <div>
      <h5>{
    
    childName}</h5>
      <button>changeChildName</button>
    </div>
  );
}

const Child = memo(getChild);

function Parent() {
    
    
  const [name, setName] = useState("farther");
  const [childName, setChildName] = useState("child");
  return (
    <div>
      <Child childName={
    
    childName} setChildName={
    
    setChildName} />
      <h4>{
    
    name}</h4>
      <button
        onClick={
    
    () => {
    
    
          setName(Math.random());
        }}
      >
        changeFartherName
      </button>
    </div>
  );
}

通过const Child = memo(getChild);将Child组件进行包装,这个时候,只有更改childName属性的时候,子组件才会更新。

React.memo同时也提供了第二个参数,接收一个回调函数,参数是新旧的props。例如想控制在什么条件下更新,在什么条件下不更新。通过方法的返回值(true || false)来决定组件的更新。

useCallback

举个例子,有以下场景。当父组件将状态值,和改变状态值的方法都传给了子组件。当父组件更新的时候,子组件从props里拿的方法是重复定义的,还是一直用的一个?

 //用来存储父组件传给子组件的setChild方法
 let arr = [];
 function getChild(props) {
    
    
   console.log("child更新");
   const {
    
     childName, setChild } = props;
   arr.push(setChild);
   //判断每次传入的setChild方法是不是同一个
   if (arr.length > 1) {
    
    
     console.log(arr[0] === arr[1]); //false 答案不是同一个
   }
   return (
     <div>
       <h5>{
    
    childName}</h5>
       <button>changeChildName</button>
     </div>
   );
 }
 
 const Child = memo(getChild);
 
 function Parent() {
    
    
   const [name, setName] = useState("farther");
   const [childName, setChildName] = useState("child");
   //更改状态值的方法,传递给子组件
   const setChild = () => {
    
    
     if(Math.random() > 2){
    
    
       setChildName(Math.random())
     }
   }
   return (
     <div>
       <Child childName={
    
    childName} setChild={
    
    setChild} />
       <h4>{
    
    name}</h4>
       <button
         onClick={
    
    () => {
    
    
           setChildName(Math.random());
         }}
       >
         changeFartherName
       </button>
     </div>
   );
 }

执行上面的代码,可以看出来,每次父组件更新,都要定义一个setChild方法。但是似乎并不需要这种重复定义,所以useCallback出现了。

useCallback接收两个参数,第一个参数是回调函数,第二参数是一个数组。返回一个处理过的回调函数。
只有数组里的依赖项改变的时候,它才会更新。也就是说上面的代码可以经过useCallback处理:

  let arr = [];
  function getChild(props) {
    
    
    console.log("child更新");
    const {
    
     childName, setChild } = props;
    arr.push(setChild);
    if (arr.length > 1) {
    
    
      console.log(arr[0] === arr[1]); //true 说明拿到的方法都是一个方法的引用
    }
    return (
      <div>
        <h5>{
    
    childName}</h5>
        <button>changeChildName</button>
      </div>
    );
  }
  
  const Child = memo(getChild);
  
  function Parent() {
    
    
    const [name, setName] = useState("farther");
    const [childName, setChildName] = useState("child");
    //通过useCallback进行处理
    const setChild = useCallback(() => {
    
    
      if(Math.random() > 2){
    
    
        setChildName(Math.random())
      }
    },[])
    return (
      <div>
        <Child childName={
    
    childName} setChild={
    
    setChild} />
        <h4>{
    
    name}</h4>
        <button
          onClick={
    
    () => {
    
    
            setChildName(Math.random());
          }}
        >
          changeFartherName
        </button>
      </div>
    );
  }

而useCallback是useMemo的一个语法糖,对于useMemo的hook这里就不细说了。

猜你喜欢

转载自blog.csdn.net/weixin_46726346/article/details/131126343