Как установить состояние в ответных хуках, только если состояние отличается от текущего значения - PullRequest
2 голосов
/ 10 апреля 2020

У меня есть следующий код

setInterval(() => {
             fetch(`/clusters?type=${type}`)
                .then((response) => response.json()
                  .then((responseBody) => {
                    if(clusters !== responseBody.members)
                        setClusters(responseBody.members);
                  }))
                .catch();
    }, 5000);

Так как clusters устанавливается асинхронно, когда проверка if(clusters !== responseBody.members) clusters всегда пуста, так какой правильный способ сделать это? Я пытаюсь сделать это, чтобы меньше рендеров, так как этот запрос происходит каждые 5 секунд

Ответы [ 2 ]

1 голос
/ 10 апреля 2020

Это потому, что состояние, которое вы получаете, уже устарело. Что вы можете сделать, это создать ссылку, используя useRef, а затем внутри другой useEffect просто обновить ссылку с текущим значением вашего состояния. А затем после получения данных используйте ref для сравнения их с состоянием, например:

function App() {
  const [clusters, setClusters] = useState([]);
  const clustersRef = useRef(clusters);

  useEffect(() => {
    clustersRef.current = clusters;
  });

  useEffect(() => {
    setInterval(() => {
      fetch(`/clusters?type=${type}`)
      .then((response) => response.json()
      .then((responseBody) => {
          if(clustersRef.current !== responseBody.members)
            setClusters(responseBody.members);
      }))
      .catch();
    }, 5000);
  }, []);
}

Надеюсь, это поможет:)

0 голосов
/ 10 апреля 2020

Вы можете передать функцию setClusters, которая получает текущее состояние в качестве аргумента. Затем вы можете сравнить и либо вернуть новые элементы, если есть что-то новое, либо старое значение, если ничего не изменилось. React достаточно умен, чтобы знать, что он не должен перерисовываться, если вы установили состояние с точным тем же значением.

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

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

[1,2,3] === [1,2,3] //-> false

Таким образом, вы должны придумать способ сказать, если у вас есть два массива содержат одинаковое содержимое. Один из способов сделать это - создать строку идентификаторов из старых и новых данных, а затем сравнить их. (Предполагая, что clusters является массивом объектов, на которых есть некоторые уникальные id). Вы создаете одно значение, которое можно сравнить с другим единственным значением, чтобы увидеть, изменилось ли что-либо.

setClusters(prevClusters => {
  const prevIds = prevClusters.map(member => member.id).sort().join('-')
  const newIds = responseBody.members.map(member => member.id).sort().join('-')

  if (newIds === prevIds) {
    return prevClusters // nothing has changed, return previous value
  } else {
    return responseBody.members // return new values.
  }
})
...