Home > Blockchain >  "Functions are not valid as a React child" during replacement react-router-dom v5 to v6
"Functions are not valid as a React child" during replacement react-router-dom v5 to v6

Time:07-04

I tried to solve this problem in several ways without success. This Router work perfect with render but when I replace by element then the App.jsx throws me an error.

If I delete a () => in Route with Homepage than Homepage component will be render, but I can't do this in Route with signin/signup.

I can't figure out where the error could be.

import React, { useState, useEffect } from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import Homepage from "./Pages/Homepage/Homepage";
import SignIn from "./Pages/Authentication/SignIn";
import SignUp from "./Pages/Authentication/SignUp";
import Dashboard from "./Pages/Dashboard/Dashboard";

import "react-toastify/dist/ReactToastify.css";
import { ToastContainer } from "react-toastify";

const App = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  const checkAuthenticated = async () => {
    try {
      const res = await fetch("/api/auth/verify", {
        method: "POST",
        headers: { jwtToken: localStorage.token },
      });

      const parseRes = await res.json();

      parseRes === true ? setIsAuthenticated(true) : setIsAuthenticated(false);
      setIsLoading(false);
    } catch (err) {
      console.error(err.message);
    }
  };

  useEffect(() => {
    checkAuthenticated();
  }, []);

  const setAuth = (boolean) => {
    setIsAuthenticated(boolean);
  };

  return (
    <>
      {isLoading ? null : (
        <BrowserRouter>
          <Routes>
            <Route
              exact
              path="/signin"
              element={(props) =>
                !isAuthenticated ? (
                  <SignIn {...props} setAuth={setAuth} />
                ) : (
                  <Navigate to="/dashboard/overview" />
                )
              }
            />
            <Route
              exact
              path="/signup"
              element={((props) =>
                !isAuthenticated ? (
                  <SignUp {...props} setAuth={setAuth} />
                ) : (
                  <Navigate to="/dashboard/overview" />
                )
              )}
            />
            <Route
              path="/dashboard"
              element={(props) =>
                isAuthenticated ? (
                  <Dashboard {...props} setAuth={setAuth} />
                ) : (
                  <Navigate to="/signin" />
                )
              }
            />
            <Route exact path="/" element={() => <Homepage />} />
          </Routes>
        </BrowserRouter>
      )}
    </>
  );
};

export default App;

CodePudding user response:

The Route component changed significantly from react-router-dom@5 to react-router-dom@6. There are no longer component or render or children function props, instead replaced by a single element prop taking a ReactNode, a.k.a. JSX.

const App = () => {
  ...

  return (
    <>
      {isLoading ? null : (
        <BrowserRouter>
          <Routes>
            <Route
              path="/signin"
              element={!isAuthenticated
                ? <SignIn setAuth={setAuth} />
                : <Navigate to="/dashboard/overview" />
              }
            />
            <Route
              path="/signup"
              element={!isAuthenticated
                ? <SignUp setAuth={setAuth} />
                : <Navigate to="/dashboard/overview" />
              )}
            />
            <Route
              path="/dashboard"
              element={isAuthenticated
                ? <Dashboard setAuth={setAuth} />
                : <Navigate to="/signin" />
              }
            />
            <Route path="/" element={<Homepage />} />
          </Routes>
        </BrowserRouter>
      )}
    </>
  );
};

export default App;

Route protection is so common that there is layout route pattern for it. Create layout routes that consume the isAuthenticated state and render null or some loading indicator while the authentication status is fetched, then conditionally renders an Outlet for the nested routes to render their content into or a redirect.

Example:

import { Navigate, Outlet } from 'react-router-dom';

const ProtectedRoutes = ({ isAuthenticated }) => {
  if (isAuthenticated === undefined) {
    return null; // or loading indicator, etc..
  }

  return isAuthenticated
    ? <Outlet />
    : <Navigate to="/signin" replace />;
}

const AnonymousRoutes = ({ isAuthenticated }) => {
  if (isAuthenticated === undefined) {
    return null; // or loading indicator, etc..
  }

  return isAuthenticated
    ? <Navigate to="/dashboard/overview" replace />
    : <Outlet />;
}

...

const App = () => {
  const [isAuthenticated, setIsAuthenticated] = useState(); // initially undefined

  useEffect(() => {
    const checkAuthenticated = async () => {
      try {
        const res = await fetch("/api/auth/verify", {
          method: "POST",
          headers: { jwtToken: localStorage.token },
        });

        const parseRes = await res.json();

        setIsAuthenticated(!!parseRes);
      } catch (err) {
        console.error(err.message);
      }
    };

    checkAuthenticated();
  }, []);

  const setAuth = (boolean) => {
    setIsAuthenticated(boolean);
  };

  return (
    <BrowserRouter>
      <Routes>
        <Route element={<AnonymousRoutes isAuthenticated={isAuthenticated} />}>
          <Route path="/signin" element={<SignIn setAuth={setAuth} />} />
          <Route path="/signup" element={<SignUp setAuth={setAuth} />} />
        </Route>
        <Route element={<ProtectedRoutes isAuthenticated={isAuthenticated} />}>
          <Route path="/dashboard" element={<Dashboard setAuth={setAuth} />} />
        </Route>
        <Route path="/" element={<Homepage />} />
      </Routes>
    </BrowserRouter>
  );
};

export default App;

Note also that because there are no render functions for the routed components that there are no longer any route props (i.e. history, location, match). These were replaced by React hooks, i.e. useNavigate, useLocation, and useParams. Use the hooks the components if they need to access these.

  • Related