Все переменные внутри useEffect обязательно должны быть перечислены как зависимости? - PullRequest
2 голосов
/ 30 октября 2019

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

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

const scrollToTop = () => window.scrollTo(0, 0)
const scrollToTopOfArticle = () => window.scrollTo(0, 200)

function App({
  isDesktop,
  selectedArticle
}) {

  useEffect(() => {
    isDesktop ? scrollToTop() : scrollToTopOfArticle()
  }, [selectedArticle])

  return (
    <div className="App">
      <h1>{selectedArticle.title}</h1>
      <p>{selectedArticle.body}</p>
    </div>
  );
}

Если я добавляю isDesktop к зависимостям, то эффект запускается всякий раз, когда пользователь изменяет размер окна между мобильным и рабочим столом, что нежелательно, но яТакже известно о догмате о том, что все, что используется внутри эффекта, должно быть указано как зависимость.

Есть какие-либо предложения о том, как согласовать эти два требования?

Ответы [ 2 ]

3 голосов
/ 30 октября 2019

Если вы хотите, чтобы useEffect() реагировал только на изменение selectedArticle, используйте isDesktop и selectedArticle для инициализации состояний компонентов. Всякий раз, когда selectedArticle изменяется, первый useEffect() обновляет оба состояния переданными реквизитами и запускает второй useEffect() для повторного запуска при следующем рендеринге.

const scrollToTop = () => window.scrollTo(0, 0)
const scrollToTopOfArticle = () => window.scrollTo(0, 200)

function App({
  isDesktop,
  selectedArticle
}) {
  const [desktop, setDesktop] = useState(isDesktop)
  const [article, setArticle] = useState(selectedArticle)

  useEffect(() => {
    if (article !== selectedArticle) {
      setDesktop(isDesktop)
      setArticle(selectedArticle)
    }
  }, [isDesktop, selectedArticle, article])

  useEffect(() => {
    if (desktop) scrollToTop()
    else scrollToTopOfArticle()
  }, [desktop, article])

  return (
    <div className="App">
      <h1>{selectedArticle.title}</h1>
      <p>{selectedArticle.body}</p>
    </div>
  )
}

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

const useLatch = (value, deps) => {
  const [state, setState] = useState(value)
  const effect = useCallback(() => { setState(value) }, [value])
  useEffect(effect, deps)
  return state
}

const scrollToTop = () => window.scrollTo(0, 0)
const scrollToTopOfArticle = () => window.scrollTo(0, 200)

function App({
  isDesktop,
  selectedArticle
}) {
  const latchedIsDesktop = useLatch(isDesktop, [selectedArticle])

  useEffect(() => {
    if (latchedIsDesktop) scrollToTop()
    else scrollToTopOfArticle()
  }, [latchedIsDesktop, selectedArticle])

  return (
    <div className="App">
      <h1>{selectedArticle.title}</h1>
      <p>{selectedArticle.body}</p>
    </div>
  )
}
1 голос
/ 30 октября 2019

Являются ли все переменные внутри useEffect абсолютно обязательными для перечисления в качестве зависимостей?

Да, иначе будет выдано предупреждение.

Поэтому лучше иметьЧеткое представление о том, что должен делать каждый эффект (то есть разделение интересов).

Таким образом, вы можете иметь два отдельных эффекта с разными зависимостями.

Что-то вроде:

useEffect(
  () => {
    scrollToTopOfArticle();
  }, [selectedArticle]
);

useEffect(
  () => {
    if (selectedArticle && isDesktop) {
      scrollToTop();
    }
  }, [isDesktop, selectedArticle]
)
...