I'm facing this issue, I tried to convert it to a function but the states
didn't work as I expected. How can I solve this? I want to create a loading effect when switching routes without causing such issues:
react-dom.development.js?ac89:67 Warning: 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 the componentWillUnmount method.
at Loading (webpack-internal:///./components/loading.js:83:9)
at MyApp (webpack-internal:///./pages/_app.js:58:27)
at StyleRegistry (webpack-internal:///./node_modules/styled-jsx/dist/stylesheet-registry.js:231:34)
here is my code:
import Router from "next/router";
import { Component } from "react";
import Loader from "./Loader";
export default class Loading extends Component {
constructor() {
super();
this.state = {
loader: false
}
}
componentDidMount(){
// Some page has started loading
Router.events.on("routeChangeStart", (url) => {
this.setState({ loader: true });
});
// Some page has finished loading
Router.onRouteChangeComplete = (url) => {
this.setState({ loader: false });
};
}
componentWillUnmount(){
}
render() {
return(
<Loader loading={this.state.loader} />
);
}
}
I also tried:
import Router from "next/router";
import { useState } from "react";
import Loader from "./Loader";
export default function Loading (){
const [loader, setLoader] = useState(false);
// Some page has started loading
Router.events.on("routeChangeStart", (url) => {
setLoader(true);
});
// Some page has finished loading
Router.onRouteChangeComplete = (url) => {
setLoader(false);
};
return <Loader loading={loader} />;
}
The code you had in componentDidMount
needs to be moved inside a useEffect
. In addition, the subscriptions to the router events need to be cleaned up when the component unmounts to prevent the error you're seeing.
It's also recommended to use the router
instance from the useRouter
hook rather than accessing the global router object directly.
Your function component should roughly look like the following.
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import Loader from "./Loader";
export default function Loading () {
const [loader, setLoader] = useState(false);
const router = useRouter();
useEffect(() => {
const handleRouteChangeStart = (url) => {
setLoader(true);
};
const handleRouteChangeComplete = (url) => {
setLoader(false);
};
router.events.on("routeChangeStart", handleRouteChangeStart);
router.events.on("routeChangeComplete", handleRouteChangeComplete);
// When the component unmounts, unsubscribe from the router events with the `off` method
return () => {
router.events.off("routeChangeStart", handleRouteChangeStart);
router.events.off("routeChangeComplete", handleRouteChangeComplete);
};
}, [router]);
return <Loader loading={loader} />;
}