I need to move redux Provider
from index.js to App.jsx.
When it was set in index.js, it was working fine, but I was asked to set it in the App
component.
The working state is the below:
index.js
import ReactDOM from "react-dom";
import { HashRouter as Router, Routes, Route } from "react-router-dom";
import { Provider } from "react-redux";
import store from "./redux/store/index";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<Router>
<Routes>
<Route path="/*" element={<App />} />
</Routes>
</Router>
</Provider>
</React.StrictMode>,
document.getElementById("app")
);
App.jsx
return (
<Routes>
{isLoggedIn ? (
<Route path="/" element={<Home onLogout={logout} />}>
<Route index element={<Information />} />
<Route path="users" element={<UserList />} />
</Route>
) : (
<Route path="/login" element={<Login onLogin={login} />} />
)}
</Routes>
);
redux store
import { createStore, combineReducers } from "redux";
import authReducer from "../reducers/authReducer";
import informationReducer from "../reducers/informationReducers";
import userListReducer from "../reducers/userListReducer";
const rootReducer = combineReducers({
auth: authReducer,
information: informationReducer,
userList: userListReducer,
});
const store = createStore(rootReducer);
export default store;
When I moved it to App
component, I got an error in the console, and nothing mounts.
Uncaught Error: could not find react-redux context value; please
ensure the component is wrapped in a<Provider>
Below is the after moving Provider to App component
index.js
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("app")
);
App.jsx
function App() {
const isLoggedIn = useSelector((state) => state.auth.isLoggedIn);
const dispatch = useDispatch();
const navigate = useNavigate();
const login = () => {
dispatch(login());
navigate("/", { replace: true });
};
const logout = () => {
dispatch(logout());
navigate("/login", { replace: true });
};
useEffect(() => {
const refresh_token = localStorage.getItem("refresh_token");
if (!refresh_token) {
navigate("/login");
return;
}
axiosInstance
.get("/auth/refresh", {
headers: {
Authorization: `Bearer ${refresh_token}`,
},
})
.then(() => {
dispatch(login());
})
.catch((error) => {
navigate("/login");
localStorage.clear();
return Promise.reject(error);
});
}, [dispatch, navigate]);
return (
<Provider store={store}>
<Router>
<Routes>
{isLoggedIn ? (
<Route path="/" element={<Home onLogout={logout} />}>
<Route index element={<Information/>} />
<Route path="users" element={<UsersList />} />
</Route>
) : (
<Route path="/login" element={<Login onLogin={login} />} />
)}
</Routes>
</Router>
</Provider>
);
}
export default App;
As a test, I deleted all the code from router and subsequent children, and just put a simple in between the provider, and no error occured
return (
<Provider store={store}>
<div> TEST </div>
</Provider>
);
What could be the problem?
Are my routes not set properly?
react-redux: 7.2.6,
react-router-dom: 6.2.1
react: 17.0.2
redux: 4.1.2
2
Answers
The
App
component can’t render the ReduxProvider
component and attempt to select state from it. The context necessarily needs to be provided from higher up the ReactTree.The problem is
const isLoggedIn = useSelector((state) => state.auth.isLoggedIn);
inApp
. If pushingProvider
down intoApp
then the selection ofisLoggedIn
also needs to be pushed down. Fortunately this is trivial to solve and also leads to a more commonly used route protection implementation.Create
PrivateRoutes
andAnonymousRoutes
components that read theisLoggedIn
state and handle protecting the routes.Example:
App
For any component to access redux context , the component need to be wrapped inside Provider .
After your new changes App component is not wrapped by the Provider , and you still are trying to access redux context , one example is on the following line
A solution would be clearly to try and wrap the App logic with the Provider , this should do it