Home > Software engineering >  React Router with dynamic path and dynamic component
React Router with dynamic path and dynamic component

Time:07-17

I'm building the app with hierarchical structure and ability to set any slug url the user wants by the admin panel. For example, if the top page has url news and the page inside has url new_telescope, the whole url would be /news/new_telescope/. I wrote the url controller to handle this, so it really doesn't matter how many levels are there in the url - I get the data of the needed page from the server. So my React Router wouldn't have predefined urls - I need to take the current url, send it to server, get the data, get the component and render it. Now it looks like this:

function getRoute() {
    url.splice(0, 1);
    adminRequest(
        'get',
        constants.SERVER_ADDRESS   `/system/url/?url=`   JSON.stringify(url)   '&version=1',
    ).then(response => {
        const CurrentComponent = lazy(() => import('./'   response.data.template));
        return <Route path={window.location.pathname} element={<CurrentComponent page={response.data}/> } />
    });
}

return (
    <>
        <Suspense fallback={<div className='preloader'><CircularProgress size='75px' /></div>}>
            <Routes>
                // trying to get the current url with proper component
                { getRoute() }
                <Route path="/admin/" element={<Admin />} />
                <Route path="/admin/login/" element={<Login />} />
                ...
            </Routes>
        </Suspense>
    </>
);

So the problem is with the getRoute function. As you can see I make a request, get the component name from the DB and use lazy to import it. In that function I make a new Route to put it to Routes. But I get errors No routes matched location with any url. And of course the component doesn't render. I thought that Suspense will wait for the first route to appear but it doesn't work that way. How can I make a Router to wait for a dynamic route?

CodePudding user response:

Issue

  1. The getRoute function doesn't return anything. Sure, the Promise chain started from adminRequest returns some JSX, but that resolved value isn't returned by the other function.
  2. React render functions are 100% synchronous functions. You can't call an asynchronous function and expect React to wait for any asynchronous code to resolve to render any content.

Solution

Use a useEffect hook to issue the side-effect of fetching the route. Unconditionally render the Route but conditionally render the element value.

const [data, setData] = useState();
const [CurrentComponent, setCurrentComponent] = useState();

useEffect(() => {
  const getRoute = async () => {
    url.splice(0, 1);
    const response = await adminRequest(
      "get",
      constants.SERVER_ADDRESS  
        `/system/url/?url=`  
        JSON.stringify(url)  
        "&version=1"
    );
    
    const CurrentComponent = lazy(() =>
      import("./"   response.data.template)
    );
    
    setData(response.data);
    setCurrentComponent(CurrentComponent);
  };

  getRoute();
}, [url]);

...

<Suspense fallback={<div className="preloader">Loading...</div>}>
  <Routes>
    <Route
      path={/* whatever the path is */}
      element={CurrentComponent ? <CurrentComponent page={data} /> : null}
    />
    <Route path="/admin/" element={<Admin />} />
    <Route path="/admin/login/" element={<Login />} />
  </Routes>
</Suspense>

Demo

Edit react-router-with-dynamic-path-and-dynamic-component

  • Related