【发布时间】:2019-07-28 00:37:21
【问题描述】:
我正在学习 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 导致重新渲染。我知道它可能不是很能赢得性能,但如果功能组件完全可能的话,我想阻止它。有什么建议/我做错了吗?
更新(已尝试修复,尚无解决方案):
下面的用户 lector 建议可能将 useState 的结果更改为 let 而不是 const 然后直接设置它let [isAnimating] = React.useState(false);。不幸的是,正如您在下面的 sn-p 中看到的那样,这也不起作用。单击红色条将开始其动画,单击橙色方块将使组件重新渲染,如果再次单击红色条,它将打印 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
【问题讨论】:
-
用于演示此问题的示例过于复杂和冗长。一个简单的数字输入就足够了。此外,请不要在问题中提供答案,因为它们可能会被忽略。你应该在下面添加一个答案。
-
随意用一个更简单的例子创建一个新问题,但这是我的用例朋友
标签: reactjs react-hooks