I have an app, and I want the app component to hold the current logged in user. I have the following routes and components.
Basically, every component in my app will make use of the user object. Sure, I can pass the user as props to each and every component, but that is not elegant. What is the proper React way of sharing the user prop globally?
const App = () => {
const [user, setUser] = useState(null);
return (
<Router>
<div className="app">
<Topbar />
<Switch>
<Route path="/login" exact component={Login} />
<Route path="/home" exact component={Home} />
<Route path="/" exact component={ShopMenu} />
<Route path="/orders">
<Orders />
</Route>
<Route path="/wishlist" exact component={Wishlist} />
<Route path="/wallet" exact component={Wallet} />
<Route path="/cart" exact component={Cart} />
</Switch>
<BottomBar />
</div>
</Router>
);
};
Take a look at React Context and more specifically useContext as you're using hooks.
The idea of context is exactly that - for you to be able to share updateable state to the descendants without having to pass it from component to component (the so called "prop-drilling").
export const UserContext = React.createContext(null);
const App = () => {
const [user, setUser] = useState(null);
return (
<Router>
<div className="app">
<UserContext.Provider value={{ user: user, setUser: setUser }}>
<Topbar />
<Switch>
{/* <routes>... */}
</Switch>
<BottomBar />
</UserContext.Provider>
</div>
</Router>
);
};
Then, in your component:
import { UserContext } from 'app';
const Topbar = () => {
const { user, setUser } = useContext(UserContext);
// use `user` here
};
If you want to have access to the setter (setUser) you can pass it through the context as well.
Simple React Global State with Hooks (Observer Design Pattern)
However "React way" was asked for, it might be helpful for all of you who seek for a simple and robust alternative.
codesandbox example
The concept is based on Yezy Ilomo's article. It just has been put into a working condition and been renamed the variables & functions to be more self-explaining.
I use it in production, in multiple projects, with this particular implementation directly (not Yezy's npm package) and it works like a charm.
Welcome to stack overflow;
Yes you can save your state globally for which react-redux library react offers in which you need to store your state globally and you can access state in any component from redux
here is the complete guide
You can also check
complete guide here of how to use redux with react
Also you can use context api
check here for context api
Related
I think it's because I'm using the latest version of react and they have changed many components, since. All I want to do is navigate to another page.
I tried wrapping it it
<Routes>
<button
className="whatevr"
onClick={() => {
navigate("../FeaturePage");
}}
>
Features
</button>
</Routes>
Based on both errors regarding the use of the useNavigate and useRoutes hooks both needing to be used within a routing context, i.e. called with in a <Router> component, it seems that you are not actually doing this.
Render the app, or these specific components within a router.
Example:
const App = () => (
<>
<Navbar />
<Routes>
<Route path="/" .... />
... other routes ...
</Routes>
</>
);
Parent component rendering App.
<BrowserRouter>
</App />
</BrowserRouter>
In react I can use this.props.history in Route components or using withRouter. But I want to access this globally from others components.
Is there any way? Thanks in advance.
Assuming you're using at least React 16.8.0 when hooks were introduced, the useHistory hook will give you access to the history instance. This is essentially the modern equivalent of the withRouter HOC you mention in your question. Is there a reason this does not fit your needs?
You mention accessing it "globally", which makes me think you're looking for something like assigning the history instance to window, which you could of course do, but the hook/hoc are the more canonical approaches to getting access to history from a component.
As the comment said, you can use react context, or redux to pass your history to all the child components.
And, here is one solution about how you can catch the history via local HOC.
component={BK} => component={RouteWithHistory(BK)}
ReactDOM.render(
<Provider store={store}>
<ThemeProvider theme={theme}>
<BrowserRouter>
<Switch>
<Route exact path="/books" component={BK} /> // Before
<Route exact path="/books" component={RouteWithHistory(BK)} /> // After
<Route path="/details/" component={DT} />
<Redirect to="/" />
</Switch>
</BrowserRouter>
</ThemeProvider>
</Provider>
,document.getElementById('root')
);
And the main function
interface Props {
history: {location: {pathname: string}};
}
const RouteWithHistory = (Component: React.FC) => (props: Props) => {
const pathname = props.history.location.pathname;
console.log(pathname);
// You can use redux here.
// Or use the react context as below.
const ThemeContext = React.createContext('');
return (
<ThemeContext.Provider value={pathname}>
<Component />
</ThemeContext.Provider>
)
}
I'm having an issue passing props through React elements (like Switch and Route). In the example below, I would like to pass all props of Dashboard component down to the Account component. Is there a way to achieve this?
App.js
<Dashboard>
<Switch>
// Dashboard props to Account component
<Route path="/account" render={props => <Account {...props} /> } exact />
<Route path="/someothercomponent" component={Someothercomponent} />
</Switch>
</Dashboard>
Dashboard.js
render() {
const children = React.Children.map(this.props.children, child => {
var router = React.cloneElement(child, { image: this.state.image });
return router;
// Like this the router Element does receive the image prop from
// the Dashboard component. Now this image prop needs to be
// passed on to the Account component.
}
I like some of the answers already present. To give you a sense of solving this problem differently and also something to learn and add to your toolbox. I would say use Context. Context provides a way to pass data through the component tree without having to pass props down manually at every level. https://reactjs.org/docs/context.html
So if you get to your Account and have to yet again pass props down this might be a good place to implement this.
When setting up correctly you could do something like this on your page. But again you aren't just passing down one you are passing down all props. And then what if you need to also pass them down on the next component <<< this is the point of Context. I would think using context is better than using a component as your state considering a stateful component is usually limited. With context, your Account component could have several children and you wouldn't have to pass props all the way down to get done what you wish to achieve.
<AppContext.Consumer>
{({prop1, prop2, prop3}) => {
}}
</AppContext.Consumer>
That's assuming you name your variable AppContext when you use React.createContext();
The idea is that passing down props at many levels can be annoying for some but using context you can bring a property in at any time without having to worry about if you passed them down correctly. Be sure to read the article in full there are times where you want to use context and times where you do not.
Yes, use render property instead.
<Route path="path" render={() => <MyComponent {...this.props} />} />
The problem is component is overriding the render props.
Remove component={Account}
I've also added brackets around (props) to improve readability
<Dashboard>
<Switch>
<Route
path="/account"
render={(props) => <Account {...props} /> }
exact
/>
<Route
path="/someothercomponent"
component={SomeOtherComponent}
/>
</Switch>
</Dashboard>
Alternatively:
const renderMergedProps = (component, ...rest) => {
const finalProps = Object.assign({}, ...rest);
return( React.createElement(component, finalProps)
);
}
const PropsRoute = ({ component, ...rest }) => {
return (
<Route {...rest} render={routeProps => {
return renderMergedProps(component, routeProps, rest);
}}/>
);
}
<Router>
<Switch>
<PropsRoute path='/login' component={Login} auth={auth} authenticatedRedirect="/" />
<PropsRoute path='/trades' component={Trades} user={user} />
</Switch>
</Router>
source
I have an issue with react router 4. And I'm not sure if its solvable:
This is my application layout:
Which basically works. But the problem is that whenever I hit /items/:id/ via the link on the left side it also matches /items. Which causes the link list in the sidebar to rerender. The solution would be to nest the routes. But this is not possible due to the interface/DOM. The left sidebar needs be independent of the item detail. And I need to split those up like:
<div>
<div className={styles.sidebar}>
<HeaderContainer />
<Switch location={location}>
<Route exact path="/" component={Homepage} />
<Route path="/items" component={Items} />
</Switch>
</div>
<div className={styles.content}>
<Route path="/items/:id" component={ItemDetail} />
</div>
</div>
Thanks a lot for your help in advance!
I had a similar layout and I used something like this
//App.jsx
<Router path="/" component={Page}>
//Page.jsx
<Layout>
<MasterView>
<DetailView>
</Layout>
//MasterView.jsx
componentDidMount() {
const { dispatch } = this.props
const data = await api.getData();
dispatch(updateDetail(data));
}
connect()(MasterView)
// DetailView.jsx
import { connect } from 'react-redux';
render() {
return <ul>{this.props.list.map((item) => <li>{item}</li>)}</ul>;
}
// map the props you need to redux state
const mapStateToProps = (state) => ({ list: state.data.list });
connect(mapStateToProps)(DetailView)
I'm using a route config as defined here. But, when using this method of router configuration how do I pass props to the components?
Looking at the following method from the docs there doesn't seem to be a way to provide props:
const RouteWithSubRoutes = (route) => (
<Route path={route.path} render={props => (
// pass the sub-routes down to keep nesting
<route.component {...props} routes={route.routes}/>
)}/>
)
Doing something like the following doesn't work. The prop doesn't get passed through:
{routes.map((route, i) => (
<RouteWithSubRoutes key={i} user={this.state.user} {...route}/>
))}
I am using React's default state management. Do I need Redux for this?
Here is example of how to use Flux + React together to pass props. I hope this is useful for you. Let me know if you have other questions.
AppComponent.js
In your AppComponent, you would need to pass only the pages you need to render.
render () {
<div>
<Route path="/login" component={Login} />
<Route exact path= "/:date?" component={Dashboard} />
</div>
}
In your app.js component, you import all services, actions, and main store component you need. Checkout bottle.js for easier way to pass values and services. However, you would need to just render
ReactDOM.render(
(
<BrowserRouter>
<Route path="/" AppStore={AppStore} render={props => <AppComponent {...props} AppStore={AppStore} />} />
</BrowserRouter>
),
document.getElementById("view-container")
);
You should let the parent component like the component Dashboard in AppComponment.js get the data passed from app.js to AppComponent to Dashboard (and Dashboard's children components).
As for AppStore, this would be like a container for all your other stores. You would need import all your other data store components and actions.