繁体   English   中英

React hooks函数组件阻止在状态更新时重新呈现

[英]React hooks function component prevent re-render on state update

我正在学习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栏(粉红色),您将看到它在动画时不会重新渲染,但是如果单击FunctionExample栏(红色),它将在动画时重新渲染两次。 这是因为我正在使用setIsAnimating导致重新渲染。 我知道它可能不是非常有性能的,但我想在功能组件完全可能的情况下阻止它。 任何建议/我做错了什么?

更新(尝试修复,还没有解决方案):在用户lecstor下面建议可能更改useState的结果而不是const然后直接设置它let [isAnimating] = React.useState(false); 遗憾的是,这不会起作用,因为您可以在下面的代码段中看到。 单击红色栏将开始它的动画,点击橙色方块将使组件重新渲染,如果你单击红色的酒吧再次将打印isAnimating重新设置为false,即使栏仍是动画。

 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而不是useState useRef可用于dom元素,实际上建议用于实例变量。 请参阅: https//reactjs.org/docs/hooks-faq.html#is-there-something-like-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> 

为什么你认为你不能像使用类一样使用内部变量?

好吧,感觉有点脏,但如何改变useState状态? 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> 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM