Реагировать - заменить ответный вызов `setState` в функциональных компонентах? - PullRequest
1 голос
/ 15 февраля 2020

Мы перешли на «Реагировать на функциональные компоненты» вместо «Компонент на основе классов». Я не могу найти замену logi c для setState функции обратного вызова . Т.е. у меня есть функциональный компонент с состоянием, и я хочу создать функцию-обработчик событий, которая последовательно изменяет состояние несколько раз, но я не знаю о текущем значении состояния (это может быть true / false). Следующий пример может иметь больше смысла.

const Example = () => {
  const [ openDoor, setOpenDoor ] = useState(false); 
  // the following handler should swich 'openDoor' state to inverse of
  // current state value. Then after setTimeout duration, inverse it again
  const toggleOpenDoor = () => {
  setOpenDoor(!openDoor);
  // within setTimeout below, '!openDoor' does not work because it still
  // receives the same value as above because of async nature of 
  // state updates
  setTimeout(() => setOpenDoor(!openDoor), 500)
  }
  return(...);
}

В class компонентах у нас был аргумент обратного вызова, который обновлял бы состояние после предыдущего обновления. Как мне добиться того же в вышеуказанном функциональном компоненте, используя хук состояния?

Ответы [ 6 ]

2 голосов
/ 15 февраля 2020

Интересно, лучше ли использовать useEffect? Особенно, когда вызов setTimeout внутри useEffect будет вызывать бесконечное l oop, так как каждый раз, когда мы вызываем setOpenDoor, приложение рендерится, а затем useEffect вызывается снова, вызывая setTimeOut, которая будет вызывать функцию setOpenDoor ... Графически:

setTimeout -> setOpenDoor -> useEffect -> setTimeout -> ... hell  

Конечно, вы можете использовать оператор if, который используетEeffect так же, как предложил @ksav, но это не выполняет sh одно требование @Kayote:

Я не знаю текущий значение состояния (может быть истинным / ложным)

Вот решение, которое работает без useEffect и выполняет sh указанное выше требование:

Код работает в codesandbox

Здесь вы увидите важность этого фрагмента кода:

const toggleOpenDoor = () => {
  setOpenDoor(!openDoor);
  setTimeout(() => setOpenDoor(openDoor => !openDoor), 500);
};

Поскольку мы используем setTimeout, нам нужно передать обратный вызов в setOpenDoor вместо обновленного состояния. Это потому, что мы хотим отправить «текущее» состояние. Если вместо этого мы отправили новое состояние, то к тому времени, когда setTimeOut обработает это состояние, оно изменится (потому что мы сделали это до того, как setTimeOut выполнит свой обратный вызов с setOpenDoor (! OpenDoor);), и никаких изменений не будет.

1 голос
/ 15 февраля 2020
import React, { useState, useEffect } from "react";

const Example = () => {
  const [openDoor, setOpenDoor] = useState(false);
  const toggleOpenDoor = () => {
    setOpenDoor(!openDoor);
  };
  useEffect(() => {
    console.log(openDoor);
    if (openDoor) {
      setTimeout(() => setOpenDoor(!openDoor), 1500);
    }
  }, [openDoor]);
  return (
    <>
      <button onClick={toggleOpenDoor}>Toggle</button>
      <p>{`openDoor: ${openDoor}`}</p>
    </>
  );
};

export default Example;

Codesandbox

1 голос
/ 15 февраля 2020

Вы должны просто использовать setTimeout в useEffect обратном вызове:

const App = () => {
  const [openDoor, setOpenDoor] = useState(false);

  const toggle = () => setOpenDoor(prevOpen => !prevOpen);

  useEffect(() => {
    const id = setTimeout(() => toggle(), 1000);
    return () => clearTimeout(id);
  }, [openDoor]);

  return <Container>isOpen: {String(openDoor)}</Container>;
};

Edit kind-dirac-b68xf

1 голос
/ 15 февраля 2020

Вы можете использовать useEffect крючок для достижения этого.

setOpenDoor(!openDoor);

useEffect(() => {
   // Here your next setState function
}, [openDoor]);

Для получения дополнительной информации о крюках, пожалуйста, проверьте https://reactjs.org/docs/hooks-effect.html

1 голос
/ 15 февраля 2020

Я скажу вам, что он работает почти так же, как this.setState, вы просто передаете функцию обратного вызова, которая принимает предыдущее состояние в качестве параметра и возвращает новое состояние ( docs )

const Example = () => {
  const [openDoor, setOpenDoor] = useState(false); 

  const toggleOpenDoor = () => {
    setOpenDoor(!openDoor);
    setTimeout(() => setOpenDoor(prevDoor => !prevDoor), 500)
  }
  return(...);
}

Чтобы вы знали, когда он меняется, вы можете использовать useEffect callback, который будет вызываться каждый раз, когда что-то меняется в массиве зависимостей ( docs )

const Example = () => {
  const [openDoor, setOpenDoor] = useState(false); 

  useEffect(() => {
    console.log('openDoor changed!', openDoor)
  }, [openDoor])

  const toggleOpenDoor = () => {
    setOpenDoor(!openDoor);
    setTimeout(() => setOpenDoor(prevDoor => !prevDoor), 500)
  }
  return(...);
}

:)

1 голос
/ 15 февраля 2020

Вы можете использовать ловушку useEffect, чтобы увидеть, когда произошло изменение состояния.

useEffect(() => { 
  // do something
  console.log('openDoor change', openDoor)
}, [openDoor]);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...