React Hooks功能组件可防止在状态更新上重新渲染



我正在学习有关React Hooks的学习,这意味着我将不得不离开类以功能组件。以前,在课堂上,我可以拥有类变量,而不是我可以更新的状态而无需重新渲染组件。现在,我正在尝试将组件重新创建为具有挂钩的功能组件,我已经遇到了我无法(据我所知)对该功能进行变量的问题,因此存储数据的唯一方法是通过useState钩。但是,这意味着每当该状态更新时,我的组件都会重新渲染。

我已经在下面的示例中对其进行了说明,我试图将类组件重新创建为使用钩子的函数组件。如果有人单击它,我想为DIV动画,但如果用户已经在动画时单击该动画。

class ClassExample extends React.Component {
  _isAnimating = false;
  _blockRef = null;
  
  onBlockRef = (ref) => {
    if (ref) {
      this._blockRef = ref;
    }
  }
  
  // Animate the block.
  onClick = () => {
    if (this._isAnimating) {
      return;
    }
    this._isAnimating = true;
    Velocity(this._blockRef, {
      translateX: 500,
      complete: () => {
        Velocity(this._blockRef, {
          translateX: 0,
          complete: () => {
            this._isAnimating = false;
          }
        },
        {
          duration: 1000
        });
      }
    },
    {
      duration: 1000
    });
  };
  
  render() {
    console.log("Rendering ClassExample");
    return(
      <div>
        <div id='block' onClick={this.onClick} ref={this.onBlockRef} style={{ width: '100px', height: '10px', backgroundColor: 'pink'}}>{}</div>
      </div>
    );
  }
}
const FunctionExample = (props) => {
  console.log("Rendering FunctionExample");
  
  const [ isAnimating, setIsAnimating ] = React.useState(false);
  const blockRef = React.useRef(null);
  
  // Animate the block.
  const onClick = React.useCallback(() => {
    if (isAnimating) {
      return;
    }
    setIsAnimating(true);
    Velocity(blockRef.current, {
      translateX: 500,
      complete: () => {
        Velocity(blockRef.current, {
          translateX: 0,
          complete: () => {
            setIsAnimating(false);
          }
        },
        {
          duration: 1000
        });
      }
    },
    {
      duration: 1000
    });
  });
  
  return(
    <div>
      <div id='block' onClick={onClick} ref={blockRef} style={{ width: '100px', height: '10px', backgroundColor: 'red'}}>{}</div>
    </div>
  );
};
ReactDOM.render(<div><ClassExample/><FunctionExample/></div>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
</div>

如果您单击ClasSexample栏(粉红色),您会发现它在动画时不会重新渲染,但是,如果您单击功能示例栏(红色),则在动画时会播放两次。这是因为我正在使用setIsAnimating,这会导致重新渲染。我知道这可能不是很好的性能,但是如果功能组件完全可以使用,我想防止它。有任何建议/我做错了什么吗?

update(尝试修复,尚无解决方案):在用户LECSTOR下方建议将USESTATE的结果更改为LET而不是const,然后将其直接设置为let [isAnimating] = React.useState(false);。不幸的是,这也不正如您在下面的摘要中所看到的那样。单击红色栏将启动动画,单击橙色广场将使组件重新渲染,如果您再次单击红色栏,它将打印isAnimating即使栏仍在动画。<<<<<<<<<<<<<</p>

const FunctionExample = () => {
  console.log("Rendering FunctionExample");
  // let isAnimating = false; // no good if component rerenders during animation
  
  // abuse useState var instead?
  let [isAnimating] = React.useState(false);
  
  // Var to force a re-render.
  const [ forceCount, forceUpdate ] = React.useState(0);
  const blockRef = React.useRef(null);
  // Animate the block.
  const onClick = React.useCallback(() => {
    console.log("Is animating: ", isAnimating);
    if (isAnimating) {
      return;
    }
    
    isAnimating = true;
    Velocity(blockRef.current, {
      translateX: 500,
      complete: () => {
        Velocity(blockRef.current, {
          translateX: 0,
          complete: () => {
            isAnimating = false;
          }
        }, {
          duration: 5000
        });
      }
    }, {
      duration: 5000
    });
  });
  return (
  <div>
    <div
      id = 'block'
      onClick = {onClick}
      ref = {blockRef}
      style = {
        {
          width: '100px',
          height: '10px',
          backgroundColor: 'red'
        }
      }
      >
      {}
    </div>
    <div onClick={() => forceUpdate(forceCount + 1)} 
      style = {
        {
          width: '100px',
          height: '100px',
          marginTop: '12px',
          backgroundColor: 'orange'
        }
      }/>
  </div>
  );
};
ReactDOM.render( < div > < FunctionExample / > < /div>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
</div>

更新2(解决方案):如果要在函数组件中具有变量,但在更新时没有将其重新渲染,则可以使用useRef而不是useStateuseRef不仅可以用于DOM元素,而且实际上建议用于例如变量。请参阅:https://reactjs.org/docs/hooks-faq.html#is-sheremething-something-instance-instance-variables

使用ref在不触发渲染

的情况下保持函数调用之间的值

class ClassExample extends React.Component {
  _isAnimating = false;
  _blockRef = null;
  
  onBlockRef = (ref) => {
    if (ref) {
      this._blockRef = ref;
    }
  }
  
  // Animate the block.
  onClick = () => {
    if (this._isAnimating) {
      return;
    }
    this._isAnimating = true;
    Velocity(this._blockRef, {
      translateX: 500,
      complete: () => {
        Velocity(this._blockRef, {
          translateX: 0,
          complete: () => {
            this._isAnimating = false;
          }
        },
        {
          duration: 1000
        });
      }
    },
    {
      duration: 1000
    });
  };
  
  render() {
    console.log("Rendering ClassExample");
    return(
      <div>
        <div id='block' onClick={this.onClick} ref={this.onBlockRef} style={{ width: '100px', height: '10px', backgroundColor: 'pink'}}>{}</div>
      </div>
    );
  }
}
const FunctionExample = (props) => {
  console.log("Rendering FunctionExample");
  
  const isAnimating = React.useRef(false)
  const blockRef = React.useRef(null);
  
  // Animate the block.
  const onClick = React.useCallback(() => {
    if (isAnimating.current) {
      return;
    }
    isAnimating.current = true
    
    Velocity(blockRef.current, {
      translateX: 500,
      complete: () => {
        Velocity(blockRef.current, {
          translateX: 0,
          complete: () => {
            isAnimating.current = false
          }
        },
        {
          duration: 1000
        });
      }
    },
    {
      duration: 1000
    });
  });
  
  return(
    <div>
      <div id='block' onClick={onClick} ref={blockRef} style={{ width: '100px', height: '10px', backgroundColor: 'red'}}>{}</div>
    </div>
  );
};
ReactDOM.render(<div><ClassExample/><FunctionExample/></div>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
</div>

为什么您认为自己不能以类似的方式使用内部变量?

好吧,感觉有点脏,但是如何突变美国态呢?8)

nope,这不按预期工作状态已重置在重新渲染

因此,由于这与组件的实际渲染无关,因此我们的逻辑需要基于动画本身。可以通过检查速度在元素被动画时在元素上设置的类来解决此特定问题。

const FunctionExample = ({ count }) => {
  console.log("Rendering FunctionExample", count);
  // let isAnimating = false; // no good if component rerenders during animation
  
  // abuse useState var instead?
  // let [isAnimating] = React.useState(false);
  
  const blockRef = React.useRef(null);
  // Animate the block.
  const onClick = React.useCallback(() => {
    // use feature of the anim itself
    if (/velocity-animating/.test(blockRef.current.className)) {
      return;
    }
    console.log("animation triggered");
    
    Velocity(blockRef.current, {
      translateX: 500,
      complete: () => {
        Velocity(blockRef.current, {
          translateX: 0,
        }, {
          duration: 1000
        });
      }
    }, {
      duration: 5000
    });
  });
  return (
  <div>
    <div
      id = 'block'
      onClick = {onClick}
      ref = {blockRef}
      style = {
        {
          width: '100px',
          height: '10px',
          backgroundColor: 'red'
        }
      }
      >
      {}
    </div>
  </div>
  );
};
const Counter = () => {
  const [count, setCount] = React.useState(0);
  return <div>
    <FunctionExample count={count} />
    <button onClick={() => setCount(c => c + 1)}>Count</button>
  </div>;
}
ReactDOM.render( < div > < Counter / > < /div>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id='root' style='width: 100%; height: 100%'>
</div>

相关内容

  • 没有找到相关文章

最新更新