How to avoid infinite loops inside useEffect working with dispatch()?

139 Views Asked by At

I'm making a project where I have a list with several properties. Each property has data such as name, latitude, longitute, among others.

On one side of the screen I render the list showing the property name. On the other side of the screen, inside the map, I render some markers that are equivalent to the latitude and longitude of each property.

As I move around my map, I would like the component where it shows the list with the name of the properties to be changed, showing only the properties that are within the visible area of the map. (In the same way as airbnb and other similar platforms already work, as I move the map or zoom in, the list of properties is updated)

I'm doing this update inside a useEffect, but I'm returning the following error when I move the map:

Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

This is my code:

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { setProperties } from "../../store/StoreInfo";

const Properties = () => {
  const dispatch = useDispatch();
  const dataProperties = useSelector((state) => state.storeInfo.dataProperties);
  const mapMoveEnd = useSelector((state) => state.storeInfo.mapMoveEnd);

  React.useEffect(() => {
    if (mapMoveEnd && dataProperties) {
      const _response = dataProperties.filter((accommodation) => {
        return mapMoveEnd
          .getBounds()
          .contains([accommodation.latitude, accommodation.longitude]);
      });
      dispatch(setProperties(_response));
    }
  }, [mapMoveEnd, dispatch, dataProperties]);

  return (
    <div>
      {dataProperties &&
        dataProperties.map((property, index) => (
          <div
            style={{
              display: "flex",
              padding: "10px",
              borderBottom: "1px solid gray"
            }}
            key={`index_${index}`}
          >
            <h3>Name: {property.name}</h3>
          </div>
        ))}
    </div>
  );
};

export default Properties;

I put my project into codesandbox.io because it's easier to understand how the components communicate. If you open the link you will see that when moving the map, the error appears afterwards.

Thank you in advance for any help!!!

enter image description here

3

There are 3 best solutions below

1
On

This Could happen when a component repeatedly calls setState inside an useEffect.Because of the dependency it creates an infinite looping. Remove the dependencies from the useEffect and it will work in a perfect manner.

Try below attached code :

 React.useEffect(() => {
    if (mapMoveEnd && dataProperties) {
      const _response = dataProperties.filter((accommodation) => {
        return mapMoveEnd
          .getBounds()
          .contains([accommodation.latitude, accommodation.longitude]);
      });
      dispatch(setProperties(_response));
    }
  }, [dataProperties]);
0
On

Looks like the ordering of dependency on useEffect is the problem but not sure.

can you try using it in the following order

[mapMoveEnd, dataProperties, dispatch]

This is because react may not be able to determine if a particular value has changed or not if you list the dependencies in a different order.

3
On
React.useEffect(() => {
if (mapMoveEnd && dataProperties) {
  const _response = dataProperties.filter((accommodation) => {
    return mapMoveEnd
      .getBounds()
      .contains([accommodation.latitude, accommodation.longitude]);
  });
  dispatch(setProperties(_response));
}
}, [mapMoveEnd, dispatch, dataProperties]);

your dependencies contain dataProperties which is changed in dispatch(setProperties(_response));. That is the reason why it loops infinitely. How about using useMemo

  const da = useMemo(() => {
let _response = dataProperties;
if (mapMoveEnd && dataProperties) {
  _response = dataProperties.filter((accommodation) => {
    return mapMoveEnd
      .getBounds()
      .contains([accommodation.latitude, accommodation.longitude]);
  });
}
return _response;
}, [dataProperties, mapMoveEnd]);

I tried and it worked. You could try yourself. Have a nice day!