React Router's useParams cause useEffect to rerun

Viewed 7979

I am using react-router 5.1 and react 16.10.

For a Master-Detail page, I am using useParams() to get the id from the url to open the page for the current group. Opening happens in a useEffect(). As a result, id has to be given as a dependency in that effect. However, when creating a new group, when there is enough data for a group to be valid, the API sends the id for that group and it sets the URL to /group/:id. As a result however, the effect runs again.

function Groups(props) {
  const { id } = useParams();
  const history = useHistory();

  const [group, setGroup] = useState(NEW_GROUP);

  const getData = useCallback(async () => {
    await Promise.resolve(Api.getGroups());
  }, []);

  const getGroup = useCallback(async group => {
    history.push(`/groups/${group.id}`);

    await Promise.resolve(Api.getGroup(group)).then(data => {
      setGroup(data.group);
    });
  }, [getData, history]);

  useEffect(() => {
    getData();

    if (id !== undefined) {
      getGroup({ id });
    }
  }, [props.actions, id, getData, getGroup]);

  // Saving data
  const saveGroup = useCallback(async () => {
    setSaved(SAVING);

    await Promise.resolve(Api.storeGroup(group)).then(data => {
        if (!group.hasOwnProperty('id')) {
            history.push(`/groups/${data.id}`);
            setGroup(prevGroup => ({ ...prevGroup, id: data.id }));
        }

        getData();
        setSaved(SAVED);
    }).catch(() => setSaved(FAILED));
  }, [getData, group, history]);
}

How do I prevent this from happening without breaking the "rules of hooks"?

1 Answers

I had the same issue, and the way I solved is by passing the id from useParams to the function and set id only in useEffect.

function Groups(props) {
  const { id } = useParams();
  ...

  useEffect(() => {
    getData(id);

    if (id !== undefined) {
      getGroup({ id });
    }
  }, [id]);

 ...
}
Related