为什么 useState 提前调用我的函数

Why is useState calling my function early

我有一个与此等效的组件:

const PickAShell = () => {
    const [functionToRun, setFunctionToRun] = useState(() => {});
    const [shellPicked, setShellPicked] = useState(0);
    const shells = [1,2,3,4,5];

    const pickShellFn = (shellNumber, onConfirm) => {
        setFunctionToRun(onConfirm);
        setShellPicked(shellNumber);
    }
    const win = () => setShellPicked(0)
      && alert('You Win');
    const lose = (shellNumber) => setShellPicked(0)
      && alert('nothing under shell '+shellNumber)
    return (
    <div>
        {shells.map(shellNumber => {
            const clickHandler = shellNumber%2?
                () => pickShellFn(shellNumber, () => win())
              : () => pickShellFn(shellNumber, () => lose(shellNumber));
            return <Button onClick={clickHandler}>{`pick shell number ${shellNumber}`}</Button> 
     }) }
    {shellPicked? (<Button onClick={functionToRun}>Reveal Shell</Button>): null}
    </div>
    )
}

我看到的是,当我 运行 它并单击 'pick shell number X' 按钮的等效项时,它 运行 的功能 (win/lose ) 早。

在 chrome 开发工具中,它正在调用 'setFunctionToRun' 并立即调用该函数(丢失)。

我期望的是,在单击说 'pick shell number 2' 按钮后,它应该将 functionToRun 设置为 () => win() 粗箭头功能。然后只有在点击 'Reveal Shell' 按钮时它才会调用任何东西。

解决方案是像这样制作 clickHandler:

const clickHandler = shellNumber%2?
            () => pickShellFn(shellNumber, () => () => win())
          : () => pickShellFn(shellNumber, () => () => lose(shellNumber));

现在调用函数时 returns 内部函数,准备好通过显示按钮调用。

这是因为useState可以调用with a function

setPartOfState(prevState => doSomethingWith(previousState))

这意味着替代方法是调用:

setFunctionToRun(() => onConfirm);

它使用相同的签名,但不使用以前的状态或调用 onConfirm。如果这个匿名函数不存在,它会在此时调用 onConfirm 而不是仅仅传递它。