Search code examples
reactjstypescriptreact-router-dom

How to use layout and provider in the same page?


I have provider which makes a request to the server to refresh a token and has some values. Also I have a simple layout with a sidebar. I wanna use them only in one route. Idk where to add the layout and I've already used Outlet in the provider

Provider:

export const AuthProvider: FC = () => {
  const [isUserLoged, setIsUserLoged] = useState(false)
  const [isAppReady, setIsAppReady] = useState(false)

  useEffect(() => {
    AuthClient.post("/refresh").then((res: AxiosResponse) => {
      const {accessToken, accessTokenExpiration} = res.data;

      inMemoryJWT.setToken(accessToken, accessTokenExpiration)

      setIsAppReady(true)
      setIsUserLoged(true)
    }).catch((err) => {
      setIsAppReady(true)
      toast.error(err.response.data.error)
    })
  }, [])

  useEffect(() => {
    const handlePersistedLogOut = (e: StorageEvent) => {
      if(e.key === config.LOGOUT_STORAGE_KEY) {
        inMemoryJWT.deleteToken()
        setIsUserLoged(false)
      }
    }

    return window.addEventListener("storage", handlePersistedLogOut)
  }, [])

  return (
    <AuthContext.Provider
      value={{
        isAppReady,
        isUserLoged,
      }}
    >

    <Outlet />
    
    </AuthContext.Provider>
  );
};

Routing:

export const Routers: FC = () => {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<AuthProvider />}>
                    <Route path="/home" element={<div>Home</div>} />
                </Route>

                <Route path="/auth" element={<Auth />} />

                <Route path="*" element={<ErrorPage message="Страница не найдена!" status={404} />} />
            </Routes>
        </BrowserRouter>
    )
}


Solution

  • There is really no need to "do it all in one route" when layout routes can be pathless. Pathless routes only contribute to the UI, nothing to the URL path.

    A trivial solution is to use separate layout routes for separate concerns.

    Example:

    export const Routers: FC = () => {
      return (
        <BrowserRouter>
          <Routes>
            <Route element={<AuthProvider />}> // <-- pathless
              <Route element={<Layout />}>     // <-- pathless
                <Route path="/home" element={<div>Home</div>} />
              </Route>
            </Route>
    
            <Route path="/auth" element={<Auth />} />
            <Route
              path="*"
              element={<ErrorPage message="Страница не найдена!" status={404} />}
            />
          </Routes>
        </BrowserRouter>
      );
    };
    

    If you have layout components that are commonly used together then you can abstract these into a single layout.

    Example:

    export const AuthProvider = ({ children }) => {
      ...
    
      return (
        <AuthContext.Provider
          value={{
            isAppReady,
            isUserLoged,
          }}
        >
          {children}
        </AuthContext.Provider>
      );
    };
    
    const Layout = ({ children }) => {
      ...
    
      return (
        ...
        {children}
        ...
    
      );
    };
    
    const AuthLayout = () => (
      return (
        <AuthProvider>
          <Layout>
            <Outlet />
          </Layout>
        </AuthProvider>
      );
    );
    
    export const Routers: FC = () => {
      return (
        <BrowserRouter>
          <Routes>
            <Route element={<AuthLayout />}> // <-- pathless
              <Route path="/home" element={<div>Home</div>} />
            </Route>
    
            <Route path="/auth" element={<Auth />} />
            <Route
              path="*"
              element={<ErrorPage message="Страница не найдена!" status={404} />}
            />
          </Routes>
        </BrowserRouter>
      );
    };