Home > Blockchain >  Can't perform a React state update with useMediaQuery
Can't perform a React state update with useMediaQuery

Time:06-07

I'm trying to use useMediaQuery with NextJS to conditionally render a background image, but i get "Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. "

when getStaticProps starts.

I tried to add some cleanup function, but with no results.

    export const getStaticProps: GetStaticProps = async () => {
  const rocketData = await fetchPlanetsInfo("technology");

  return {
    props: {
      data: rocketData,
    },
  };
};


export default function Technology({ data }: Props) {
  const isMobile = useMediaQuery({ query: "(max-width: 30em)" });
  const [mobileView, setMobileView] = React.useState(false);

  React.useEffect(() => {
    setMobileView(isMobile);
  }, [isMobile]);

  return (
    <Layout>
      <MainPagesComponents
        backgroundImage={spaceLaunchBackground}
        title={"03 SPACE LAUNCH 101"}
      >
        <Swiper
          // direction={"vertical"}
          modules={[Pagination, Autoplay]}
          spaceBetween={0}
          slidesPerView={"auto"}
          centeredSlides={true}
          scrollbar={{ draggable: true }}
          autoplay={{
            delay: 5000,
            pauseOnMouseEnter: true,
            disableOnInteraction: false,
          }}
          pagination={{
            clickable: true,
            bulletActiveClass: "tech-active-class",
            bulletClass: "swiper-tech",
            horizontalClass: "swiper-tech-position-container",
            renderBullet: (index, className) => {
              return `
            <div class='${className}'>${index   1}</div>
            `;
            },
          }}
          className="mySwiper"
        >
          {data.map((rocket) => {
            const portraitImage = rocket.images.portrait.slice(1);
            const landscapeImage = rocket.images.landscape.slice(1);
            const viewImage = {
              width: mobileView ? "375px" : "515px",
              height: mobileView ? "170px" : "527px",
            };
            return (
              <SwiperSlide key={uuidv4()}>
                <TechnologySlider
                  view={viewImage}
                  image={mobileView ? landscapeImage : portraitImage}
                  title={rocket.name.toUpperCase()}
                  description={rocket.description}
                />
              </SwiperSlide>
            );
          })}
        </Swiper>
      </MainPagesComponents>
    </Layout>
  );

UPDATE:

I create a simple gif where i'm recording the error, https://gifyu.com/image/SHbRd

CodePudding user response:

Is the error occurring when you call the getStaticProps function? If that's the case, you need to fix how you're returning the prop data. Maybe throw in a promise after the fetch occurs and then return the data if it's successful.

Also to clean up a function in react, the useEffect needs to return a callback function. I think just wrapping the useMediaQuery inside This callback function is run every time the component unmounts. Here's an example:

useEffect(() => {
  //do something
  ...
  //clean up
  return () => {
    //thing you want to clean up
  }
}, [])

You can read more about it here: https://reactjs.org/docs/hooks-effect.html

CodePudding user response:

I you need to check if the render is mounted to perform state update.

here's a custom hook that helps you check if the component is mounted, you can use it as condition to run states update and avoid the error

//our custom hook
export const useIsMounted = () => {
  const ref = React.useRef(false)
  const [, setIsMounted] = React.useState(false)
  React.useEffect(() => {
    ref.current = true
    setIsMounted(true)
    return () => (ref.current = false)
  }, [])
  return () => ref.current
}

then use import it or use it on same page file

...
  const isMobile = useMediaQuery({ query: "(max-width: 30em)" });
  const [mobileView, setMobileView] = React.useState(false);
  const isMounted = useIsMounted()

  React.useEffect(() => {
    //update state only if the component is mounted

    if(isMounted) setMobileView(isMobile); 

  }, [isMobile, isMounted ]); //add it as dependency here
...
  • Related