reactjs - 从特定函数更新状态时防止 useEffect 重新触发
问题描述
我有以下组件:
import React, { useEffect, useState } from 'react';
const UndoInput = () => {
const [name, setName] = useState('');
return(
<div className = 'Name'>
<input
onChange = {(e) => setName(e.target.value)}
value = {name}>
</input>
<Undo
name = {name}
setName = {setName}
/>
</div>
);
}
export default UndoInput;
该<Undo/>
组件是一个缓冲区,用于存储每次更改时输入的值,允许执行撤消操作。
const Undo = ({name, setName}) => {
const [buffer, setBuffer] = useState(['', '', '']);
const [index, setIndex] = useState(-1);
useEffect(() => {
let copy = [...buffer, name].slice(-3);
let pos = index + 1;
if(pos > 2)
pos = 2;
setBuffer(copy);
setIndex(pos);
}, [name]);
const undo = () => {
let pos = index - 1;
if(pos < 0)
pos = -1;
setName(buffer[pos]);
setIndex(pos);
}
return(
<button onClick = {undo}>Undo</button>
);
}
例子:
如果用户在输入中键入“abc”,buffer = ['a', 'ab', 'abc']
并且index = 2
.
当用户点击按钮时,组件应该回到之前的状态。这意味着,buffer = ['a', 'ab', 'abc']
和index = 1
。
单击按钮后,setName(buffer[pos])
执行,重新useEffect()
触发:buffer = ['ab', 'abc', 'ab']
和index = 2
(不需要)。
useEffect()
如果钩子被函数重新触发,我如何防止重新触发钩子undo()
?
解决方案
我不会为此使用 useEffect 。在我看来,您的撤消缓冲区不是 React DOM 意义上的副作用,而是您想要应用于状态更改的内部逻辑。同样,您的 Undo 组件是一个按钮,它也恰好具有关于历史和状态的可重用逻辑,可以说它更多地属于父级而不是按钮本身。
总的来说,我认为这种方法不是一个干净的自上而下的流程,并且容易在 React 中导致这种问题。
相反,我会先将您的撤消逻辑和状态放在父级中:
const handleChange = function(e){
// add to undo buffer here
setName(e.target.value);
}
const handleUndo = function(){
// fetch your undo buffer here
setName(undoValue);
}
onChange = {handleChange}
onClick = {handleUndo}
在连接完所有内容之后,您可能会觉得这样做失去了 Undo 组件的干净可重用性,而且您是对的。但这正是Custom Hooks的用途:可重用的有状态逻辑。有很多方法可以解决它,但这里有一个您可以编写的自定义钩子的示例:
const [name, setName, undoName] = useUndoableState('');
钩子将替换 useState(并在钩子内部使用它自己),管理缓冲区,并提供一个额外的函数来调用撤销并设置新名称。
<input onChange={setName} />
<button onClick={undoName}>Undo</button>
推荐阅读
- android - 如何更改 FloatingActionButton 中矢量资源的颜色?
- javascript - 如果它的子元素被聚焦,我如何隐藏一个 div?
- java - RS512 不支持的算法
- python - 找到“连接”矩阵的最大最小值的最快方法
- javascript - 在 react js 中渲染新组件 onClick 的方法
- azure - Azure 数据工厂 - 从文件共享中读取文件路径列表并保存到 Blob
- pyspark - 自动建议停止在我的笔记本上工作
- flutter - 当手机处于睡眠状态时,Flutter StreamBuilder 超时
- jmeter - jmeter 命令行选项不选择我的参数
- python - Django 我需要采取任何措施来保护我的用户密码吗?