Почему useState вызывает мою функцию рано - PullRequest
0 голосов
/ 25 июня 2019

У меня есть компонент, который эквивалентен этому:

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>
    )
}

Что я вижу, так это то, что когда я запускаю его и нажимаю эквивалент кнопки «выбрать номер оболочки X», она запускает функцию (выигрыш / проигрыш) раньше.

В инструментах chrome dev он вызывает 'setFunctionToRun' и немедленно вызывает функцию (потерять).

Что я ожидаю, так это то, что после нажатия кнопки «выбрать оболочку № 2» она должна установить для functionToRun жирную стрелку () => win(). И только тогда, когда вы нажимаете кнопку «Показать оболочку», она должна вызывать что-либо.

1 Ответ

0 голосов
/ 25 июня 2019

Решение состоит в том, чтобы сделать clickHandler следующим образом:

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

Теперь, когда функция вызывается, она возвращает внутреннюю функцию, готовую к вызову кнопкой открытия.

Этопотому что useState можно вызвать с функцией

setPartOfState(prevState => doSomethingWith(previousState))

Это означает, что альтернативой было бы вместо вышеуказанного:

setFunctionToRun(() => onConfirm);

Который использует ту же самую подпись, но не использует предыдущее состояние или вызывает onConfirm.Если бы этой анонимной функции не было, в этот момент она бы вызывала onConfirm, а не просто передавала ее.

...