So I'm trying to do make a SecureRoute
component as mentioned in this answer (they call it PrivateRoute
). I'd like to use the createBrowserRouter function in index.js. The problem is that the child of DashboardPresenter, so the DashboardView that is, when wrapped in SecureRoute does not render. Some imports omitted below for brevity.
index.js:
import SecureRoute from "./utils/SecureRoute";
import SecurityProvider from "./utils/SecurityProvider";
import { createBrowserRouter, RouterProvider} from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: <App />,
errorElement: <ErrorView />,
},
{
path: "/signup",
element: <SignUpPresenter />,
errorElement: <ErrorView />,
},
{
path: "/login",
element: <LogInPresenter />,
errorElement: <ErrorView />,
},
{
path: "/dashboard",
element: (
<SecureRoute>
<DashboardPresenter /> //Could this be the problem?
</SecureRoute>
),
errorElement: <ErrorView />,
},
]);
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<SecurityProvider>
<RouterProvider router={router} />
</SecurityProvider>
</Provider>
</React.StrictMode>
);
SecureRoute:
import React from "react";
import LogInPresenter from "../presenters/LogInPresenter";
import { Navigate, Outlet } from "react-router-dom";
import useSecurity from "./useSecurity";
const SecureRoute = () => {
const { loggedIn } = useSecurity();
return loggedIn ? <Outlet /> : <Navigate to="login" />;
};
export default SecureRoute;
useSecurity:
import { React, useContext } from "react";
import SecurityContext from "./SecurityContext";
const useSecurity = () => useContext(SecurityContext);
export default useSecurity;
SecurityContext:
import React from "react";
const SecurityContext = React.createContext({});
export default SecurityContext;
SecurityProvider:
import { React } from "react";
import SecurityContext from "./SecurityContext";
import { useDispatch, useSelector } from "react-redux";
const SecurityProvider = (props) => {
const dispatch = useDispatch();
const {
//We could use the current data in redux if we had a need for it in the view.
data: results,
error,
loading,
} = useSelector((state) => state.user || {});
return (
<SecurityContext.Provider
value={{
logIn: (email, password) => {
dispatch({
type: "LOGIN",
payload: { email: email, password: password },
});
},
logOut: () => {
dispatch({ type: "LOGOUT" });
},
signUp: (email, password) => {
dispatch({
type: "SIGNUP",
payload: { email: email, password: password },
});
},
loggedIn: results,
loading: loading,
error: error,
}}
>
{props.children}
</SecurityContext.Provider>
);
};
export default SecurityProvider;
LogInPresenter:
import { React, useState, useEffect } from "react";
import LogInView from "../views/LogInView";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import useSecurity from "../utils/useSecurity";
function LogInPresenter() {
const [email, setEmail] = useState();
const [password, setPassword] = useState();
const navigate = useNavigate();
const { logIn, loggedIn, loading, error } = useSecurity();
function loggedInACB() {
console.log(`Log the user in with ${email} and ${password}.`);
logIn(email, password);
}
useEffect(() => {
if (loggedIn && !error && !loading) {
navigate("/dashboard");
}
return () => {};
}, [loggedIn, error, loading, navigate]);
return (
<LogInView
loggedIn={loggedInACB}
loading={loading}
setEmail={setEmail}
setPassword={setPassword}
/>
);
}
export default LogInPresenter;
DashboardPresenter:
import DashboardView from "../views/DashboardView";
import useSecurity from "../utils/useSecurity";
function DashboardPresenter() {
const { logOut, loggedIn, loading, error } = useSecurity();
function logOutACB() {
console.log("Log the user out.");
logOut();
}
return <DashboardView logOut={logOutACB} loading={loading} />;
}
export default DashboardPresenter;