Как правильно использовать useEffect hook с зависимостью массива. Я передал состояние из хранилища Redux, и мой компонент рендерится бесконечно - PullRequest
1 голос
/ 18 апреля 2019

Я использую ловушку useEffect и получаю список данных пользователей с помощью вызова извлечения, используя функцию getStoreUsers , которая отправляет действие в ответ и сохраняет shopUsers (который является массивом) внутри магазин редуксов.

В зависимости от массива я пишу [shopUsers] . Я не знаю, почему это вызывает бесконечный рендеринг.

Вот как я использую ловушку useEffect:

useEffect(() => {
    const { getStoreUsers, shopUsers } = props;
    setLoading(true);
    getStoreUsers().then(() => {
      setLoading(false);
    }).catch(() => {
      setLoading(false);
    });
  }, [shopUsers]);

Я хочу перерисовать компонент только при изменении данных в массиве shopUsers.

Если я напишу shopUsers.length в зависимости от массива. Остановка для повторного рендеринга.

Но давайте предположим, что у меня есть страница, которая открывается, когда пользователь нажимает на список пользователей и обновляет данные пользователя на следующей странице. После обновления я хочу, чтобы пользователь вернулся к тому же компоненту, который ранее не был размонтирован. Таким образом, в этом случае длина массива остается неизменной, но данные внутри индекса массива обновляются. Так что shopUsers.length не будет работать в этом случае.

Ответы [ 2 ]

2 голосов
/ 18 апреля 2019

Вы можете сделать пользовательский хук, чтобы сделать то, что вы хотите:

В этом примере мы заменим последний элемент в массиве и увидим вывод в консоли.

import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { isEqual } from "lodash";

const usePrevious = value => {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};

const App = () => {
  const [arr, setArr] = useState([2, 4, 5]);
  const prevArr = usePrevious(arr);

  useEffect(() => {
    if (!isEqual(arr, prevArr)) {
      console.log(`array changed from ${prevArr} to ${arr}`);
    } 
  }, [prevArr]);

  const change = () => {
    const temp = [...arr];
    temp.pop();
    temp.push(6);
    setArr(temp);
  };

  return (
      <button onClick={change}>change last array element</button>
  )
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Живой пример здесь .

2 голосов
/ 18 апреля 2019

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

Я думаю, что вы хотите оптимизировать эторендеринг самого компонента, поскольку вы уже используете redux, я предполагаю, что ваши реквизиты / состояния неизменны, поэтому вы можете использовать React.memo для повторного рендеринга вашего компонента только при изменении одного из его реквизитов.

Также вы должны определить переменную состояния / props вне ваших ловушек, так как они используются в области действия всей функции следующим образом.

В вашем случае, если вы передаете пустой массив как второйparam to memo, то он будет срабатывать только на ComponentDidMount, если вы передадите null / undefined или ничего не передадите, он сработает на ComponentDidMount + ComponentDidUpdate, если вы хотите оптимизировать его так, что даже при изменении реквизита / обновлении компонента хук не выполняетне срабатывает, если не изменяется конкретная переменная, тогда вы можете добавить некоторую переменную в качестве второго аргумента

React.memo(function(props){
  const [isLoading, setLoading] = useState(false);
  const { getStoreUsers, shopUsers } = props;
  useEffect(() => {
    setLoading(true);
    getStoreUsers().then(() => {
      setLoading(false);
    }).catch((err) => {
      setLoading(false);
    });
  }, []);
...
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...