Реакция перехватывает использование очистки эффекта () только для компонента WillUnmount? - PullRequest
15 голосов
/ 06 марта 2019

Позвольте мне объяснить результат этого кода, чтобы легко задать мою проблему.

const ForExample = () => {
    const [name, setName] = useState('');
    const [username, setUsername] = useState('');

    useEffect(() => {
        console.log('effect');
        console.log({
            name,
            username
        });

        return () => {
            console.log('cleaned up');
            console.log({
                name,
                username
            });
        };
    }, [username]);

    const handleName = e => {
        const { value } = e.target;

        setName(value);
    };

    const handleUsername = e => {
        const { value } = e.target;

        setUsername(value);
    };

    return (
        <div>
            <div>
                <input value={name} onChange={handleName} />
                <input value={username} onChange={handleUsername} />
            </div>
            <div>
                <div>
                    <span>{name}</span>
                </div>
                <div>
                    <span>{username}</span>
                </div>
            </div>
        </div>
    );
};

Когда ForExample component монтируется, будет зарегистрирован «эффект». Это связано с componentDidMount().

И всякий раз, когда я изменяю ввод имени, будут записываться как «эффект», так и «очищенный». И наоборот, никакое сообщение не будет регистрироваться всякий раз, когда я меняю ввод имени пользователя, так как я добавил [username] ко второму параметру useEffect(). Это связано с componentDidUpdate()

Наконец, когда ForExample component размонтируется, в журнал будет добавлено «очищено». Это связано с componentWillUnmount().

Мы все это знаем.

В итоге, 'cleaned' вызывается всякий раз, когда компонент перерисовывается (включая unmount)

Если я хочу, чтобы этот компонент регистрировался «очищенным» только на тот момент, когда он не подключен, мне просто нужно изменить второй параметр useEffect() на [].

Но если я изменю [username] на [], ForExample component больше не реализует componentDidUpdate() для ввода имени.

Я хочу сделать так, чтобы компонент поддерживал как componentDidUpdate() только для ввода имени, так и componentWillUnmount(). (ведение журнала «очищено» только в тот момент, когда компонент демонтируется)

Ответы [ 2 ]

18 голосов
/ 07 марта 2019

вы можете использовать более одного useEffect

например, если моя переменная data1, я могу использовать все это в моем компоненте

useEffect( () => console.log("mount"), [] );
useEffect( () => console.log("will update data1"), [ data1 ] );
useEffect( () => console.log("will update any") );
useEffect( () => () => console.log("will update data1 or unmount"), [ data1 ] );
useEffect( () => () => console.log("unmount"), [] );
11 голосов
/ 06 марта 2019

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

Пример

const { useState, useEffect } = React;

const ForExample = () => {
  const [name, setName] = useState("");
  const [username, setUsername] = useState("");

  useEffect(
    () => {
      console.log("effect");
    },
    [username]
  );

  useEffect(() => {
    return () => {
      console.log("cleaned up");
    };
  }, []);

  const handleName = e => {
    const { value } = e.target;

    setName(value);
  };

  const handleUsername = e => {
    const { value } = e.target;

    setUsername(value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={handleName} />
        <input value={username} onChange={handleUsername} />
      </div>
      <div>
        <div>
          <span>{name}</span>
        </div>
        <div>
          <span>{username}</span>
        </div>
      </div>
    </div>
  );
};

function App() {
  const [shouldRender, setShouldRender] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setShouldRender(false);
    }, 5000);
  }, []);

  return shouldRender ? <ForExample /> : null;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
...