我有两个input
其值反射(reflect)计数器的状态。
第一个input
当其值发生变化时立即设置状态。这个input
可以代表应用程序的其他部分改变状态。
第二个input
我制作的名为 LazyInput
的组件的一部分。 <强> LazyInput
不会立即更新状态,仅在模糊时才更新,我认为这提供了更好的可用性。此输入可以表示用户输入,例如要设置的计时器或屏幕上某些内容的位置。
但是,我想要 LazyInput
不要表现得“懒惰”,并且实际上立即更新计数器仅当通过 ↑ ↓ 键更改值时。
App
代码(包括正常的 input
)如下:
const {Fragment, useState, useEffect} = React;
function LazyInput({ name, value, onComplete }) {
const initialValue = value;
const [lazyValue, setLazyValue] = useState(value);
// Sync to state changed by the regular input
useEffect(() => {
setLazyValue(value);
}, [value]);
const handleKeyDown = e => {
const { key, target } = e;
switch (key) {
case "ArrowUp": {
setLazyValue(parseFloat(lazyValue) + 1);
onComplete(e);
break;
}
case "ArrowDown": {
setLazyValue(parseFloat(lazyValue) - 1);
onComplete(e);
break;
}
case "Escape": {
setLazyValue(initialValue);
target.blur();
break;
}
case "Enter": {
target.blur();
break;
}
default:
return;
}
};
return (
<input
name={name}
value={lazyValue}
onChange={e => setLazyValue(e.target.value)}
onKeyDown={e => handleKeyDown(e)}
onBlur={e => onComplete(e)}
/>
);
}
function App() {
const [counter, setCounter] = useState(0);
function handleCounterChange(e) {
setCounter(parseFloat(e.target.value));
}
return (
<Fragment>
<div>Counter: {counter}</div>
<LazyInput
name="counter"
value={counter}
onComplete={e => handleCounterChange(e)}
/>
<input
value={counter}
type="number"
onChange={e => handleCounterChange(e)}
/>
</Fragment>
);
}
ReactDOM.render(<App />, document.getElementById("app"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="app"></div>
问题是:按 ↑ ↓ 键会触发 onComplete(e)
setLazyValue()
之前函数– 我想是因为它是异步的 – 与状态失去同步。
出于同样的原因,按 Esc 会模糊 input
在将其值重置为 initialValue
之前.
我知道setState()
类回调has been substituted by the useEffect()
hook ,但是:
- 如何调用
onComplete(e)
功能 – 与任何其他事件一样 – 来自useEffect()
? - 如何仅在按下 ↑ ↓ 键而不是所有其他键时执行此操作?
预先感谢您的帮助,这是 interactive sandbox .
最佳答案
我建议将 counter
和 setCounter
传递给 LazyInput
,以便可以直接访问它们。 useEffect
可以被赋予一个依赖项数组,只有当它的依赖项之一发生变化时它才会触发。但是,useEffect
不适合这种情况。您可以将 counter
指定为 useEffect
的依赖项,但每次 counter
的值发生更改时它都会触发。 useEffect
只能监视变量,不能向其传递值。我建议阅读这篇文章,了解 useEffect
的工作原理以及何时使用它。
https://overreacted.io/a-complete-guide-to-useeffect/
我的解决方案将 counter
和 setCounter
传递给 LazyInput
。如果您希望 LazyInput
保留其自己的状态,可以使用 useEffect
来监视 lazyValue
。当lazyValue
改变时,counter
应该更新。
这是我的解决方案:
const {Fragment, useState, useEffect} = React;
function LazyInput({ name, counter, setCounter }) {
const [initialValue] = useState(counter);
const handleKeyDown = e => {
const { key, target } = e;
switch (key) {
case "ArrowUp": {
setCounter(counter + 1);
break;
}
case "ArrowDown": {
setCounter(counter - 1);
break;
}
case "Escape": {
setCounter(initialValue);
target.blur();
break;
}
case "Enter": {
target.blur();
break;
}
default:
return;
}
};
return (
<input
name={name}
value={counter}
onChange={e => setCounter(e.target.value)}
onKeyDown={e => handleKeyDown(e)}
/>
);
}
function App() {
const [counter, setCounter] = useState(0);
return (
<Fragment>
<div>Counter: {counter}</div>
<LazyInput name="counter" counter={counter} setCounter={setCounter} />
<input
value={counter}
type="number"
onChange={e => setCounter(parseFloat(e.target.value))}
/>
</Fragment>
);
}
ReactDOM.render(<App />, document.getElementById("app"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="app"></div>
关于javascript - 使用 React hooks 设置状态后如何从组件调用事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60119101/