[英]How to call event from component after setting its state with React hooks?
I have two input
s whose values reflect the state of a counter.我有两个input
,它们的值反映了计数器的状态。
The first input
sets the state immediately as its value changes.第一个input
在其值更改时立即设置状态。 This input
can represent other parts of the application changing state.此input
可以表示应用程序更改状态的其他部分。
The second input
part of a component I made named LazyInput
.我创建的名为LazyInput
的组件的第二个input
部分。 LazyInput
doesn't update state immediately , only when it's blurred, which I think gives a better usability. LazyInput
不会立即更新状态,只有在状态模糊时才更新,我认为这提供了更好的可用性。 This input can represent the user input, like a timer to be set or the position of something on the screen.此输入可以表示用户输入,例如要设置的计时器或屏幕上某物的位置。
However, I would like the LazyInput
not to behave "lazily" and actually update the counter immediately only when the value is changed via the ↑ ↓ keys .但是,我希望LazyInput
不要“懒惰”地运行,并且仅在通过↑ ↓键更改值时才实际立即更新计数器。
The App
code (which includes the normal input
) is the following: 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>
The problem is: pressing the ↑ ↓ keys fires the onComplete(e)
function before the setLazyValue()
– I suppose because it's asynchronous – losing sync with the state.问题是:按下↑ ↓键会在setLazyValue()
之前触发onComplete(e)
函数——我想是因为它是异步的——与状态失去同步。
For the same reason, pressing Esc blurs the input
before resetting its value to initialValue
.出于同样的原因,在将input
值重置为initialValue
之前按Esc 会模糊input
。
I know the setState()
class callback has been substituted by the useEffect()
hook , but:我知道setState()
类回调已被useEffect()
hook 替代,但是:
onComplete(e)
function – like any other event – from useEffect()
?我如何从useEffect()
调用onComplete(e)
函数——就像任何其他事件一样?Thanks in advance for you help, here's an interactive sandbox .预先感谢您的帮助,这是一个交互式沙箱。
I would recommend passing counter
and setCounter
down to LazyInput
so they can be accessed directly.我建议将counter
和setCounter
传递给LazyInput
以便可以直接访问它们。 useEffect
can be given a dependency array where it will only fire if one of its dependencies changes. useEffect
可以被赋予一个依赖数组,只有当它的一个依赖改变时它才会触发。 However, useEffect
is not right for this situation.但是, useEffect
不适合这种情况。 You could assign counter
as a dependency of the useEffect
but it would fire every time the value of counter
is changed.您可以将counter
指定为useEffect
的依赖项,但每次更改counter
的值时它都会触发。 useEffect
can only watch variables, you can't pass values to it. useEffect
只能观察变量,不能向它传递值。 I suggest reading this article for learning about how useEffect
works and when to use it.我建议阅读这篇文章以了解useEffect
工作原理以及何时使用它。
https://overreacted.io/a-complete-guide-to-useeffect/ https://overreacted.io/a-complete-guide-to-useeffect/
My solution passes counter
and setCounter
to LazyInput
.我的解决方案将counter
和setCounter
传递给LazyInput
。 If you want LazyInput
to retain its own state, you can use a useEffect
to watch lazyValue
.如果您希望LazyInput
保留自己的状态,您可以使用useEffect
来观察lazyValue
。 When lazyValue
changes, the counter
should be updated.当lazyValue
改变时, counter
应该更新。
Here is my solution:这是我的解决方案:
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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.