С помощью useEffect, как я могу пропустить применение эффекта к начальному рендеру? - PullRequest
0 голосов
/ 06 ноября 2018

С помощью новых React Effect Hooks я могу сказать React пропустить применение эффекта, если некоторые значения не изменились при повторном рендеринге - пример из документов React:

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

Но в приведенном выше примере эффект применяется при первоначальной визуализации и при последующих повторных визуализациях, когда count изменилось. Как я могу сказать React пропустить эффект на начальном рендере?

Ответы [ 4 ]

0 голосов
/ 23 мая 2019

Вот пользовательский хук, который просто предоставляет логический флаг, чтобы указать, является ли текущий рендеринг первым рендером (когда компонент был смонтирован). Он примерно такой же, как и некоторые другие ответы, но вы можете использовать флаг в useEffect или в функции рендеринга или в любом другом месте компонента, который вы хотите. Может быть, кто-то может предложить лучшее имя.

import { useRef, useEffect } from 'react';

export const useIsMount = () => {
  const isMountRef = useRef(true);
  useEffect(() => {
    isMountRef.current = false;
  }, []);
  return isMountRef.current;
};

Вы можете использовать его как:

import React, { useEffect } from 'react';

import { useIsMount } from './useIsMount';

const MyComponent = () => {
  const isMount = useIsMount();

  useEffect(() => {
    if (isMount) {
      console.log('First Render');
    } else {
      console.log('Subsequent Render');
    }
  });

  return isMount ? <p>First Render</p> : <p>Subsequent Render</p>;
};

А вот тест, если вам интересно:

import { renderHook } from 'react-hooks-testing-library';

import { useIsMount } from '../useIsMount';

describe('useIsMount', () => {
  it('should be true on first render and false after', () => {
    const { result, rerender } = renderHook(() => useIsMount());
    expect(result.current).toEqual(true);
    rerender();
    expect(result.current).toEqual(false);
    rerender();
    expect(result.current).toEqual(false);
  });
});

Наш вариант использования заключался в том, чтобы скрыть анимированные элементы, если начальный реквизит указывает, что они должны быть скрыты. На более поздних рендерах, если реквизиты изменились, мы хотели, чтобы элементы оживили.

0 голосов
/ 01 марта 2019
Подход

@ estus - это хорошо, но большую часть времени я делаю его более простым, без необходимости использовать ссылки.

Ключевым моментом является то, что вы используете useEffect большую часть времени для выборки или вычисления некоторых данных / компонентов, поэтому вы можете просто вернуть false (ничего не будет отображаться) или загрузить спиннер при первом рендеринге и после выборки / вычисления (useEffect) - компонент рендеринга.

function nicknameComponent({reader}) {
  const [nickname, nicknameSet] = useState(false)

  useEffect(() => {
    let res = await fetch('api/reader')
    res = await res.json()
    nicknameSet(res)
  }, [reader]);

  if (!nickname) return false
  //or show loading spinner
  //if (!nickname) return <LoadingSpinner>

  return <div>{nickname}</div>
}
0 голосов
/ 29 марта 2019

Я использую обычную переменную состояния вместо ссылки.

// Initializing didMount as false
const [didMount, setDidMount] = useState(false)

// Setting didMount to true upon mounting
useEffect(() => setDidMount(true), [])

// Now that we have a variable that tells us wether or not the component has
// mounted we can change the behavior of the other effect based on that
const [count, setCount] = useState(0)
useEffect(() => {
  if (didMount) document.title = `You clicked ${count} times`
}, [count])

Мы можем реорганизовать логику didMount как пользовательский хук, подобный этому.

function useDidMount() {
  const [didMount, setDidMount] = useState(false)
  useEffect(() => setDidMount(true), [])

  return didMount
}

Наконец, мы можем использовать его в нашем компоненте следующим образом.

const didMount = useDidMount()

const [count, setCount] = useState(0)
useEffect(() => {
  if (didMount) document.title = `You clicked ${count} times`
}, [count])
0 голосов
/ 07 ноября 2018

Как говорится в руководстве,

Хук эффектов, useEffect, добавляет возможность выполнения побочных эффектов из функционального компонента. Он служит для тех же целей, что и componentDidMount, componentDidUpdate и componentWillUnmount в классах React, но объединен в единый API.

В этом примере из руководства ожидается, что count равен 0 только при начальном рендеринге:

const [count, setCount] = useState(0);

Так что он будет работать как componentDidUpdate с дополнительной проверкой:

useEffect(() => {
  if (count)
    document.title = `You clicked ${count} times`;
}, [count]);

Вот как может работать пользовательский хук вместо useEffect:

function useDidUpdateEffect(fn, inputs) {
  const didMountRef = useRef(false);

  useEffect(() => {
    if (didMountRef.current)
      fn();
    else
      didMountRef.current = true;
  }, inputs);
}

Кредиты идут в @Tholle за предложение useRef вместо setState.

...