How can I use React Context with react-router-dom? - javascript

AuthContext:
import { createContext } from "react";
export const AuthContext = createContext(null);
App.js:
const App = () => {
const isLogin = false;
if (isLogin) {
return (
<RouterProvider router={privateRoute} />
);
} else {
return (
<RouterProvider router={publicRoute} />
);
}
};
export default App;
I try to put <RouterProvider> in AuthContext like this:
<AuthContext.RouterProvider router={privateRoute} />
and like this:
<AuthContext>
<RouterProvider router={privateRoute} />
</AuthContext>
None of these options worked. What am I doing wrong?

One has nothing to do with the other. The issue is that AuthContext doesn't have any RouterProvider property nor is the code you are trying to use actually using a React context Provider component and value.
Just wrap the App component with the AuthContext.Provider component and ensure a context value is passed.
Example:
<AuthContext.Provider value={{ ..... }}>
<App />
</AuthContext.Provider>

you are trying to use the AuthContext with the RouterProvider but AuthContext have no property or component called RouterProvider.
To use AuthContext in combination with the RouterProvider, you will need to wrap the RouterProvider component in a component that consumes the AuthContext's value (i.e. useContext(AuthContext)).
Here a snippet to help you out...
In this we are using AuthContext value to detemine the value of routes and then use it with RouterProvider
const App = () => {
const auth = useContext(AuthContext);
const routes = auth.isLoggedIn ? privateRoute : publicRoute;
return (
<RouterProvider router={routes} />
);
};
export default App;
inside and you need to wrap the App component inside AuthContext like ...
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<AuthContext>
<App />
</AuthContext>
</React.StrictMode>
);

Related

Why I receive the error message when trying to access user data from the context?

I need to access the user data from AuthDataContext inside App.jsx, the problem is I receive an error message, most probably because there is no access of the user context into App.jsx . Can someone see the mistake?
// Authentication Context
import { createContext, useState, useEffect } from "react";
import {
EmailAuthCredential,
getAuth,
onAuthStateChanged,
} from "firebase/auth";
export const AuthDataContext = createContext(null);
export default function AuthContext({ children }) {
const auth = getAuth();
const [user, setUser] = useState(null);
onAuthStateChanged(auth, (user) => {
if (user) {
return setUser(user);
} else {
return setUser(null);
}
});
return (
<AuthDataContext.Provider value={{ user, setUser }}>
{children}
</AuthDataContext.Provider>
);
}
// App
...other code
import ContextProviders from "./context/Context-Config";
import AuthContext from "./context/Auth-Context";
import { AuthDataContext } from "./context/Auth-Context";
export default function App() {
...other code
const { user, setUser } = useContext(AuthDataContext);
return (
<AuthContext>
<ContextProviders
configs={configs}
genres={{
genresMovie,
genresTv,
}}
bookmarkShows={{
bookmarkShows,
setBookmarkShows,
bookmarksTrace,
setBookmarksTrace,
}}
movies={{
trendingMovies,
popularMovies,
topRatedMovies,
upcomingMovies,
nowPlayingMovies,
}}
tv={{
popularTv,
topRatedTv,
airingTodayTv,
onTheAirTv,
}}
>
<div className="App">
<Navbar />
<SearchBar />
<Outlet />
</div>
</ContextProviders>
</AuthContext>
);
}
**the error message : **
Uncaught TypeError: Cannot destructure property 'user' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is null.
at App (App.jsx:42:1)
Maybe I organized the context incorrectly? Why I can't access data into my App.jsx if I wrapped it into AuthContext?
The problem was, I wrapped with AuthContext inside my App.jsx:
<AuthContext>
<ContextProviders
configs={configs}
genres={{
genresMovie,
genresTv,
}}
bookmarkShows={{
bookmarkShows,
setBookmarkShows,
bookmarksTrace,
setBookmarksTrace,
}}
movies={{
trendingMovies,
popularMovies,
topRatedMovies,
upcomingMovies,
nowPlayingMovies,
}}
tv={{
popularTv,
topRatedTv,
airingTodayTv,
onTheAirTv,
}}
>
<div className="App">
<Navbar />
<SearchBar />
<Outlet />
</div>
</ContextProviders>
</AuthContext>
The solution was to move AuthContext into index.js and wrap there the Router, which by the way contains the app component as well:
import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import { router } from "./route-config";
import AuthContext from "./context/Auth-Context";
import App from "./App";
import "./assets/styles/index.scss";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<AuthContext>
<RouterProvider router={router} />
</AuthContext>
</React.StrictMode>
);

React showing a blank screen without any error messages (context api used)

I'm building a react Amazon clone, so in order to give the user the ability to add a product into his basket (as the first version of all the future checkout system), I've used Context Api to manage this.
When I finished writing Context Api code and adding it to the index.js file so I could access the data I got a blank screen with absolutely no error messages. I don't know where is exactly the problem.
StateProvider.js
import React , { createContext, useContext, useReducer} from 'react'
export const StateContext = createContext();
export const StateProvider = ({reducer , initialValue, children}) => {
<StateContext.Provider value={useReducer(reducer, initialValue)}>
{children}
</StateContext.Provider>
};
export const useStateValue = () => useContext(StateContext)
reducer.js
export const initialState = {
basket: [],
};
const reducer = (state, action) => {
switch(action.type) {
case "ADD_TO_BASKET":
return{
...state,
basket: [...state.basket, ...action.item]
};
default:
return state;
};
};
export default reducer;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { StateProvider } from './Special_components/StateProvider';
import reducer, { initialState } from './Special_components/reducer';
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<StateProvider reducer={reducer} initialState={initialState} >
<App />
</StateProvider>
</React.StrictMode>
)
Looks like you're providing props to the wrong component.
Try this in index.js:
...
<StateProvider reducer={reducer} initialValue={initialState}>
<App />
</StateProvider>
...
Also note how I renamed the prop to initialValue because that's what your StateProvider is expecting.
You forgot to add return in your StateProvider function
export const StateProvider = ({reducer , initialValue, children}) => {
return (
<StateContext.Provider value={useReducer(reducer, initialValue)}>
{children}
</StateContext.Provider>
)}
export const useStateValue = () => useContext(StateContext)
You must change arrow ES6 {} => () because {} need return
export const StateProvider = ({reducer , initialValue, children}) => (
<StateContext.Provider value={useReducer(reducer, initialValue)}>
{children}
</StateContext.Provider>
);

React Native, Redux can't find context

I'm new to redux and trying to grasp the concept of it. I'm receiving the error when redux is looking for context value. I'm sure that I wrapped the whole application in the Provider but still don't understand why it still results in the following error:
Error: could not find react-redux context value; please ensure the component is wrapped in a Provider
This is my themeReducer.js file:
const initialState = {
theme: "dark",
}
const themeReducer = (state = initialState, action) => {
if (action.type === "changeTheme"){
if (state.theme === "dark"){
return {theme: "light"};
} else {
return {theme: "dark"}
}
}
return state
}
export default themeReducer
This is my index.js for the store:
import {createStore, combineReducers} from 'redux';
import themeReducer from './reducers/themeReducer'
const rootReducer = combineReducers({
theme: themeReducer
})
const store = createStore(rootReducer)
export default store
Lastly, this is where I wrapped the root component with the provider to access the state:
import "react-native-gesture-handler";
import React from "react";
import { ThemeProvider } from "styled-components";
import AppNavContainer from "./src/navigations/index";
import { Provider } from "react-redux";
import { useSelector } from "react-redux";
import store from "./src/store/index"
export default function App() {
const currentTheme = useSelector(state => state.theme)
return (
<Provider store={store}>
<ThemeProvider theme={{ mode: {currentTheme} }}>
<AppNavContainer />
</ThemeProvider>
</Provider>
);
}
I'm using the styled component to access the theme value to change the background colour of my custom components. How I understand redux is that if I wrap everything under the Provider, I can access that state anywhere underneath that Provider. Did I do something wrong? Any opinions are appreciated
return (
<Provider store={store}>
<ThemeProvider theme={{ mode: {currentTheme} }}>
<AppNavContainer />
</ThemeProvider>
</Provider>
);
The store (and thus useSelector) is only available to components that are farther down the component tree from the Provider. So in ThemeProvider, in AppNavContainer, and anything inside AppNavContainer. You cannot use useSelector in App, because there is no Provider farther up the tree from App.
You'll probably want to split this component into multiple components. For example:
export default function App() {
return (
<Provider store={store}>
<ThemeWrapper />
</Provider>
);
}
function ThemeWrapper () {
const currentTheme = useSelector(state => state.theme)
return (
<ThemeProvider theme={{ mode: {currentTheme} }}>
<AppNavContainer />
</ThemeProvider>
);
}

React Native - Nothing was returned from render

My application is stored in /src/index.js but i also have a /App.js and a /index.js.
I don't know the difference between these and i think thats the reason im getting this error.
/index.js
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('client', () => App);
/App.js
import App from './src/index';
export default App;
/src/index.js
import React from 'react';
import { AppRegistry } from 'react-native';
import { Provider, connect } from 'react-redux';
import { addNavigationHelpers } from 'react-navigation';
import Navigator from './routes/route';
import store from './store/configureStore';
const App = ({ dispatch, nav }) => {
<Navigator
navigation={addNavigationHelpers({
dispatch,
state: nav,
})}
/>
};
const mapStateToProps = state => ({
nav: state.nav,
});
const AppWithNavigation = connect(mapStateToProps)(App);
export default () => {
<Provider store={store}>
<AppWithNavigation />
</Provider>
}
I used create react native package to build this project and then tried to follow some guides to implement react navigation with redux.
Your default export is not returning anything :
export default () => {
<Provider store={store}>
<AppWithNavigation />
</Provider>
}
To return JSX with an arrow function you need to use () => ( <JSX /> ) or the equivalent with curly braces : () => { return ( <JSX /> ) } :
export default () => (
<Provider store={store}>
<AppWithNavigation />
</Provider>
)
or :
export default () => {
return (
<Provider store={store}>
<AppWithNavigation />
</Provider>
)
}
You forgot to return the components
const App = ({ dispatch, nav }) => {
return(
<Navigator
navigation={addNavigationHelpers({
dispatch,
state: nav,
})}
/>
)
};
export default () => {
return(
<Provider store={store}>
<AppWithNavigation />
</Provider>
)
}
I didn't mention this
import React from 'react';
and all other react-native components in my other files of screens.
Because I was calling my screen component from another file, from App.js file, so I also had to import react and react-native components in that file too.

React Router and Redux - Data on initial load

I have looked a lot of questions and answers on the site concerning an issue that I am experiencing.
I have the usual setup with React, React Router and Redux. My top level component is as follows.
// Imports
const reducers = {
main: baseReducer,
form: formReducer,
};
const reducer = combineReducers(reducers);
const store = createStore(
reducer,
applyMiddleware(thunk),
);
store.dispatch(actions.auth.setCurrentUser());
// store.dispatch(actions.api.fetchLineup());
ReactDOM.render(
<Provider store={store}>
<HashRouter>
<App />
</HashRouter>
</Provider>
,
document.getElementById('root')
);
Inside my App.jsx I have the following code:
import React from 'react';
import Main from './Main';
const App = () => (
<div>
<Main />
</div>
);
export default App;
My Main.jsx
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import GroupingContainer from '../containers/Grouping';
import HomeContainer from '../containers/Home';
const Main = () => (
<main>
<Switch>
<Route exact path='/' component={HomeContainer} />
<Route path='/groupings/:id' component={GroupingContainer} />
</Switch>
</main>
);
export default Main;
And finally I have my Grouping.jsx and GroupingContainer.jsx
import React, { Component } from 'react';
function loadGrouping(props, groupingId) {
const grouping = props.main.groupings.find(
(g) => g.id === groupingId
);
if (!grouping) {
props.dispatch(api.fetchGrouping(groupingId));
}
}
class Grouping extends Component {
constructor(props) {
super(props);
loadGrouping(props, props.groupingId);
}
componentWillReceiveProps(nextProps) {
console.log('Next: ', nextProps);
if (nextProps.match && nextProps.match.params.id !== this.props.match.params.id) {
loadGrouping(this.props, nextProps.groupingId);
}
}
render() {
const grouping = this.props.main.groupings.find(
(lg) => lg.id === this.props.groupingId
);
return (
<div>
<h1>{grouping.name}</h1>
</div>
);
}
}
export default Grouping;
GroupingContainer.jsx
import { connect } from 'react-redux';
import Grouping from '../components/Grouping';
const mapStateToProps = (state, ownProps) => {
return {
groupingId: parseInt(ownProps.match.params.id, 10),
...state,
};
};
const mapDispatchToProps = (dispatch) => ({
dispatch,
});
const GroupingContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(Grouping);
export default GroupingContainer;
After the request it fires another action that adds the returned grouping to the store and into an array of groups state.main.groups
I am having 2 problems. When I browse from the root path to one of the groupings, the following flow:
http://localhost:3000 -> http://localhost:3000/#/groupings/19
I receive the message: Uncaught TypeError: Cannot read property 'name' of undefined for a brief second until the API request finishes and populates the {grouping.name} and when I do a complete refresh of the page on a grouping URL http://localhost:3000/#/groupings/19 the application does not load at all and gives them same Uncaught TypeError: Cannot read property 'name' of undefined
I have been using React for around 2 months and have really started using API requests on Component loads. I can not really figure out where to place the API request properly to prevent the view rendering before it has finished and erroring out.
Any help would be appreciated!
Thanks!
Try to change render of Grouping Component like this.
render() {
const grouping = this.props.main.groupings.find(
(lg) => lg.id === this.props.groupingId
);
return (
<div>
<h1>{grouping ? grouping.name : ""}</h1>
</div>
);
}

Categories