Passing props to component inside router - reactjs

I want to use URL paths for my app. Currently I just render a Main component in app.js:
render() {
return (
<Main
isLoggedIn={this.state.isLoggedIn}
user={this.state.user}
... />
)
}
(The props are a bunch of variables and functions)
I tried using react-router but I don't understand how to send props to the child components.
<Router history={browserHistory} >
<Route path='/' component={Main}
... where to send props? />
<Route path='join' component={Join}
... props />
</Router>
The problem is that I need some state variables of my App component (in app.js) to be available in both Main and Join. How do I achieve that with a router?
App structure:
App
- Join
- Main
- ...

It depends how you are managing your state. if you use the Redux, you don't need to pass props in router, you can simple access redux in any component using connect, However if you don't use react then you can use the Redux router's hierarchy functionality
Assume you have a route something like this
<route path="/student" component={studentHome}>
<route path="/class" component={classHome} />
</route>
when you access the path
/student/class
React router will load the classHome component as children of StudentHome and you can simple pass props to classHome component.
you student class Render method will be something like
render(){
const childrenWithProps = React.Children.map(this.props.children,
(child) => React.cloneElement(child, {
doSomething: this.doSomething
})
);
return <div> some student component functionality
<div>
{childrenWithProps}
</div>
</div>
}
more details about passing props to children:
How to pass props to {this.props.children}

You can use standard URL parameters. For example, to pass an ID:
<Route path='join/:id' component={Join}/>
You'll need to use browserHistory:
browserHistory.push(`/join/${id}`);
Then use this.props.params.id on Join.
Here's a more in-depth explanations https://stackoverflow.com/a/32901866/48869

Related

What is the simplest way to pass state while using React Router?

What is the simplest way to pass state while using React Router? My Navi component below is reflecting user being null, as opposed to user being "KungLoad". Thanks.
class App extends Component{
constructor() {
super();
this.state = {user: "KungLoad"};
}
render () {
return(
<div>
<Router>
<Route exact path="/" state component = {Navi} />
</Router>
The simplest way is that you can pass the state as props and then use it in the specified component. For your case, you have to use render instead of component for passing the state as props.
<Route exact path="/" render={() => <Navi user={this.state.user} />} />
This will work but I would recommend to you that the Context API concept of reactJS would be best suited here. You can pass the state or props to all the component using the data provider and all the components will consume the state or props that are being provided by the parent component. . https://reactjs.org/docs/context.html
version 6 react-router-dom
I know the question got answered but I feel this might be helpful example for those who want to use functional components and they are in search of passing data between components using react-router-dom v6.
Let's suppose we have two functional components, first component A, second component B. The component A wants to share data to component B.
usage of hooks: (useLocation,useNavigate)
import {Link, useNavigate} from 'react-router-dom';
function ComponentA(props) {
const navigate = useNavigate();
const toComponentB=()=>{
navigate('/componentB',{state:{id:1,name:'sabaoon'}});
}
return (
<>
<div> <a onClick={()=>{toComponentB()}}>Component B<a/></div>
</>
);
}
export default ComponentA;
Now we will get the data in Component B.
import {useLocation} from 'react-router-dom';
function ComponentB() {
const location = useLocation();
return (
<>
<div>{location.state.name}</div>
</>
)
}
export default ComponentB;
Note: you can use HOC if you are using class components as hooks won't work in class components.
Yiu can pass your state as props to your Navi component like this: <Route exact path="/" render={() => <Navi user={this.state.user} />} />
The other answers are correct, you should pass state down to children components via props. I am adding my answer to highlight one additional way that the Route component can be used. The code looks cleaner and is easier to read if you simply add children to a Route component, rather than use the render or component prop.
class App extends Component {
constructor(props) {
super(props);
this.state = {
user: "KungLoad"
};
}
render() {
return (
<div>
<Router>
<Route exact path="/">
<Navi user={this.state.user} />
</Route>
</Router>
</div>
);
}
}
After making the state and assigning value
this.state = {user: "KungLoad"};
Passing the state value to the router is done like this.
<Router>
<Route exact path="/" render={()=> (<Navi user={this.state.user}/>)} />
</Router>
Or if you want to user is not logged in use a redirect
<Route exact path="/signin" render={()=> (<Redirect to='/signin'/>)}/>

How to pass state up to Router component?

I'm pretty new to React and I'm trying to create a simple game. All the data is in state in App component. I want to redirect page to Battle component with couple of props using Router. Can I pass state from App up to the Router so Router can pass it down to Battle component like this:
<Route path="/game/battle" component={
<Battle
monster = {this.props.details} //some props living
team = {this.props.team} //in App component
player={this.props.player} //can i pass it here somehow?
}
/>
Components look like this:
Router => App(state) => Battle and somewhere after onClick(change path) i want Router => Battle(with data from App state) => return to App after battle. Is there a way to achieve it?
Thanks to everyone interested
EDIT render prop fixed the issue, thanks. Router renders data passed via props but is it a good idea to pass data from other component state to Router like this:
<App //this is where state with monster, team, player lives
(...)
<Router
monster = {this.state.details} //some props living in this App component
team = {this.state.team} //can i pass it to Router somehow
player={this.state.player} //so Router renders Battle component
//with monster, team, player data?
/>
/>
EDIT 2
Ok, so Im trying to place Router component inside App component and pass some props to it from App state. In this App component also I change path to /battle using onClick. In Router I pass props using render props but it crashed app for good. I guess I messed something up...
Router.js:
(...)
<Route path="/game/:storeId" component={App} />
<Route
path="/battle"
component={() => (
<Battle
player={this.props.player}
team={this.props.team}
monster={this.props.monster}
/>
)}/>
<Route component={NotFound} />
</Switch>
</BrowserRouter>
(...) export default Router;
App.js:
(...)
state = {
player: {},
monsters: {},
locations: {},
team: {},
battleMode: false,
communicate: "asdad",
merchants: {},
quests: {},
items: {},
};
(...) return(
<Router
monster= {this.state.monster} //some props living in this App component
team = {this.state.team} //can i pass it to Router somehow
player={this.state.player} //so Router renders Battle component
//with monster, team, player data?
/>
<button onClick={()=>{this.props.history.push(`/battle`)}}>Go →</button>
(...)
)
Solution: follow this sandbox, it's great:
codesandbox.io/s/cranky-keller-hbig1?file=/src/App.js
You can use the render prop
<Route
path="/game/battle"
render={() => (
<Battle
monster={this.props.details}
team={this.props.team}
player={this.props.player}
/>
)}
/>
Alternatively you can still use component prop but this will cause mounting of new components
<Route
path="/game/battle"
component={() => (
<Battle
monster={this.props.details}
team={this.props.team}
player={this.props.player}
/>
)}
/>

Difference between passing component to Route as prop and wrapping component in render function

What is the difference between routing to a component like this:
<Route path="coolPath" component={MyComponent} />
or
<Route path="coolPath" render={props => <MyComponent {...props} customProp="s" } />
To this:
<Route path"=coolPath">
<MyComponent />
</Route>
or
<Route path"=coolPath">
<MyComponent cusomProps="cp"/>
</Route>
first you should read through this site:
https://reacttraining.com/react-router/web/api/Route
But to explain, there's three things going on here, the first two are examples of routing with previous version of react-router (before v5) and the third is react-router (v5 - current) recommended approach.
1. Route with component
<Route path="/coolPath" component={MyComponent} />
This type of route renders the single component passed to the prop. If an inline function is passed to the Route's component prop, it will unmount and remount the component on every render via the use of React.createElement. This can be inefficient, and passing custom props via this method is only possible via an inline function. React Router's authors recommend using the render prop as opposed to the component prop for handling inline functions, as shown below.
2. Route with render
<Route path="/coolPath" render={props => <MyComponent {...props} customProp="s" } />
Instead of having a new React element created for you using the component prop with an inline function, this route type passes in a function to be called when the location matches and does not unmount a component and remount a brand new one during rerender. It's also much easier to pass custom props via this method.
3. Route with children as components
<Route path="/coolPath">
<MyComponent customProp="s" />
</Route>
This is currently the recommended approach to routing, the child components will be rendered when the path is matched by the router. It's also very easy to pass custom props with this method.
Keep in mind there is a fourth type, which is:
4. Route with children as function
From reacttraining.com:
import React from "react";
import ReactDOM from "react-dom";
import {
BrowserRouter as Router,
Link,
Route
} from "react-router-dom";
function ListItemLink({ to, ...rest }) {
return (
<Route
path={to}
children={({ match }) => (
<li className={match ? "active" : ""}>
<Link to={to} {...rest} />
</li>
)}
/>
);
}
ReactDOM.render(
<Router>
<ul>
<ListItemLink to="/somewhere" />
<ListItemLink to="/somewhere-else" />
</ul>
</Router>,
node
);
Sometimes you need to render whether the path matches the location or not. In these cases, you can use the function children prop. It works exactly like render except that it gets called whether there is a match or not.

React: how can you share the state between views using react-router?

I am using react-router to route to two views which are independent from each other. Currently I've been using a new state object in Home and Backend but I want to get rid of this because both include nested components.
class App extends Component {
render() {
return (
<Router>
<div className="App">
<Route exact={true} path="/" component={Home} />
<Route path="/app/:user_id" component={Backend} />
</div>
</Router>
);
}
}
How is it possible to share the state between the views without using other frameworks like redux or flux?
lift the state up into your 'App' component, then pass down whatever elements of the state each component needs through their props.
For example your state in App might look like:
App.state = {
homeRelatedStuff: {...},
backendRelatedStuff: {...},
sharedStuff: {...}
}
Then when declaring which component to use, you pass down props:
<Route path={"/"} component={() => <Home homeStuff={this.state.homeRelatedStuff} sharedStuff={this.state.sharedStuff}/>}/>
*Note the slightly different method of listing the component as a function so you can pass props
Your state doesn't have to keep them separate like I have, it could be flatter and you pass down multiple props to each component

How do I pass props to non-child component with react router?

I have a component which cannot traditionally inherit props from a parent component. This component is rendered via a route and not by a parent, I am talking about the <Single /> component which is the 'detail' component in this setup:
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={ProfileList} />
<Route path="/profile/:username" component={Single} /></Route>
</Router>
Props are available in the ProfileList component and that component renders a Profile component like so:
/* ProfileList render method */
render() {
return (
<div>
{this.state.profiles.map((profile, i) =>
<Profile {...this.state} key={i} index={i} data={profile} />)}
</div>
);
}
I am trying to reuse the Profile component in both the ProfileList and Single component:
<Link className="button" to={`/profile/${username}`}>
{name.first} {name.last}
</Link>
But in the Single component I have no way of accessing state or props - so I have no way of rendering this details view. I know I can either use redux for passing global state or use query parameters in my Link to=""
But I don't want tor reach out for redux yet and don't feel right about query params. So how do I access something like this.props.profiles in my Single component?
the redux connect() can completely do the job. I think you should use it, because "in fine" you will reimplement a redux connect like

Resources