1

I am fetching data using RTK Query but when errors occur I create a toast using react-toastify library that shows a toast message multiple times how to prevent this? when I consol log I see that part of 'if' condition render multiple times.

import FoodBox from "../components/FoodBox";

import styled from "styled-components";
import { useFetchFoodsQuery } from "../store";
import { useSelector } from "react-redux";
import Skeleton from "./Skeleton";
import { toast } from "react-toastify";

const Foods = styled.div`
  overflow-y: auto;
  overflow-x: hidden;
  height: 370px;
  padding: 0 30px;
`;

function FoodList({ shopId }) {
  let { data: foods, error, isFetching } = useFetchFoodsQuery(shopId);
  const user = useSelector((state) => state.user.currentUser);
  let content;
  if (isFetching) {
    content = (
      <Foods>
        <Skeleton w="22.5rem" h="3.5rem" times={5} />
      </Foods>
    );
  } else if (error) {
    toast('Error');
    return <div>Error in loading food</div>;
  } else {
    if (!user) {
      foods = foods?.filter((food) => food.isVisible);
    }
    content = (
      <Foods>
        {foods?.map((food) => (
          <FoodBox key={food?._id} food={food} />
        ))}
      </Foods>
    );
  }

  return content;
}

export default FoodList;

Output: enter image description here

Het Patel
  • 109
  • 7

1 Answers1

1

The code is calling toast as an unintentional side-effect outside the React component lifecycle. The React component render method (yes, the entire React function component body is the "render method") is to be considered a pure function, free of side-effects. "Double rendering" most likely is happening from rendering the app into the React.StrictMode component where it intentionally double invokes specific lifecycle methods and double mounts the component as a debugging method to help you find issues in your code, like unexpected side-effects.

See:

Dispatch the toast from an expected lifecycle method, e.g. the useEffect hook. The useEffect is "one side-effect per component render to the DOM".

Example:

function FoodList({ shopId }) {
  const { data: foods, error, isFetching } = useFetchFoodsQuery(shopId);
  const user = useSelector((state) => state.user.currentUser);

  // Issue intentional side effect
  React.useEffect(() => {
    if (error) {
      toast('Error');
    }
  }, [error]);

  if (isFetching) {
    return (
      <Foods>
        <Skeleton w="22.5rem" h="3.5rem" times={5} />
      </Foods>
    );
  } else if (error) {
    return <div>Error in loading food</div>;
  } else {
    return (
      <Foods>
        {foods?.filter((food) => !!user || food.isVisible
          .map((food) => <FoodBox key={food?._id} food={food} />)
        }
      </Foods>
    );
  }
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • @HetPatel Can you clarify specifically what isn't working? Is it still triggering two toasts? Think you could create a ***running*** [codesandbox](https://codesandbox.io/) demo that reproduces the issue that we could inspect live? – Drew Reese May 12 '23 at 05:29
  • @HetPatel Oh, you deleted your comment and accepted the answer. Is it working as expected for you now? – Drew Reese May 12 '23 at 05:33
  • Ya, it works as accepted when I remove Strict Mode but inside strict mode when the first time the site loads it still shows two toasts after I refresh any number of times it shows only one toast. is that ok? – Het Patel May 12 '23 at 06:06
  • @HetPatel I wouldn't remove the `StrictMode` component. This would sort of be expected behavior if you manually double-mounted/rendered the component as well, but luckily the `StrictMode` component only does this in non-production builds. If you really wanted to, you could add some state *somewhere* that tracks when a toast for a specific error has been toasted once, so you can prevent any subsequent toasts for the same message/error. Does this make sense? – Drew Reese May 12 '23 at 06:14