In my main component, I have have an array of components that I pass to a child component (in a route) as a prop (this.state.commonProps.leftNavItems). When I navigate to the child's route and the child component renders, I noticed that the components passed to it as a prop are not the same instances but are instead rendered as new components (new componentDidMount) using their default values (not the current state as exist in the parent). Is there a way to pass the component instances themselves and keep their state, or can you only pass the component definitions and so would end up mounting brand new components when you pass them as props to children?
<Route
exact
path="/"
render={props => (
<Layout
leftNavItems={this.state.commonProps.leftNavItems}
/* mainItems={simulationRunRequestsMainItems} */
mainItems={null}
/>
)}
/>
<Route
exact
path="/simulation-runs"
render={props => (
<div>
<SimulationRun
commonProps={this.state.commonProps}
refreshSimulationRuns={this.refreshSimulationRuns}
/>
</div>
)}
Related
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.
I ran into an issue with HotModuleReloading, using ReactRouter for the first time. The browser console would display the correct change updates, but the window itself would not update.
From the official docs:
When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop (below).
I read that as render reduces the amount of unnecessary re-renders, here are their docs:
This allows for convenient inline rendering and wrapping without the undesired remounting explained above.Instead of having a new React element created for you using the component prop, you can pass in a function to be called when the location matches. The render prop receives all the same route props as the component render prop.
I had been using the render method like so:
const App = () => {
return (
<Switch>
<Route exact path="/" render={() => <Home />} />
</Switch>
);
};
I tried removing my Redux <Provider> content, but no changes. So I swapped out render for component like so and it works fine:
const App = () => {
return (
<Switch>
<Route exact path="/" component={Home} />
</Switch>
);
};
So, why is this?? What am I missing?
When you use component the Route component passes certain props to the rendered component - such as location, history, match etc.
When you use render you're rendering the component in JSX like <Home />. This doesn't have any props passed to it.
If for whatever reason, you'd need to use render over component, you should pass the same props as component does:
const App = () => {
return (
<Switch>
<Route exact path="/" render={(props) => <Home ...props />} />
</Switch>
);
};
This will make sure Home gets the props it needs to deal with Router stuff.
Hope this helps you get to the bottom of it!
const Home = () => <div>Home</div>
const App = () => {
const someVariable = true;
return (
<Switch>
{/* these are good */}
<Route exact path='/' component={Home} />
<Route
path='/about'
render={(props) => <About {...props} />}
/>
</Switch>
)
}
const About = (props) => {
return (
<div>
About
</div>
)
}
In the code sample , at
<Route
path='/about'
render={(props) => <About {...props} />}
/>
when react encounters the render prop of the Route component which is part of react-router, what does it pass a props?
Given the documentation at https://reactjs.org/docs/render-props.html ,
a render prop is a function prop that a component uses to know what to render,
is the value passed a props buried inside the declaration of Route in react-router
The props are passed to the render prop method by the Route component. You can see this in the React Router source code. The props passed by the Route component have match, location, history, staticContext. If you want to use props from the parent component, where you are defining the render props method then you can omit the props argument.
render={() => <About {...props} />}
Then you would get the props from the component that contains the Route.
The example you have provided doesn't make much sense since that replicates the behaviour that you get by just using the 'component' prop on the Route.
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Route.js#L120
We use Route with render props as,
<Route path = "/about" component={About} />
OR,
<Route path = "/about" render= { (props) => <About {...props} } />
The second one is different from the first one in the sense that in the second case, the About component has access to the props coming through the Route.
Say, for instance,
there is a Profile component,
<Route path="/admin/profile"
render={ props => (
<Profile tabs= {"valuePassed"} {...props} />
)}
/>
Now in Profile component, we can access all the props,
this.props.tabs give "valuePasses" in class-based component while props.tabs is used for functional component.
Hope this helps.
You get react router default props while passing props in render method just like if use component instead of using render props which implicitly get all these props match, location, history and staticContext. and you need to provide props as an argument otherwise it render method won't pass props down to the children because it will consider it undefined.
Here is working example for render props in react router:
https://codesandbox.io/s/72k8xz669j
This Route is using the render prop to render a child component with props:
<Route to='/path/:id' render={ () => (<ChildComponent myProp={myProp} match={this.props.match}/>)} />
But the version of match passed seems to be 'matching' against the parent component's route state and is thus not registering id underneath match.route.params.
I figured some solution like this might synchronize the route state:
<Route to='/path/:id' render={ () => (withRouter(<ChildComponent myProp={myProp} />))} />
But that just leads to an error saying that child components cannot be functions...
What's the correct way to deal with this?
Since you want to pass the match parameter, you shouldn't pass the one available to parent but the one relevant to the Route. render prop callback receives the Router Props which you can pass down
<Route to='/path/:id' render={(routerProps) => (<ChildComponent {...routerProps} />)} />
or you can simply write
<Route to='/path/:id' component={ChildComponent} />
in which case it will pass on the relevant router props to the component.
and in the child component you can access the match props like
this.props.match.params.id
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