How can I display nested component with React router? - reactjs

I’m using the last release of React Router and I want to know what is the best solution to show different component that are nested in a parent component.
I’ll try to explain myself better.
I have a route R with path /r1.
This route loads component A.
Component A has inside others 3 components B, C and D that I should show in the same page with component A only when the user press a specific button in component A.
So I want to be able to add a description in route R to manage this. For example to show component B the router could be /r1/b.
Now I did this with a state variable inside component A but I think should be better if I can use some React Router property.
Thanks

You can create nested routes component, and it will manage nested routes.
export default function NestedRoutes() {
return (
<Switch>
<Redirect exact from={"/r1"} to={`/r1/A`} />
<Route path={`/r1/A`}>
<ComponentA />
</Route>
<Route path={`/r1/B`}>
<ComponentB />
</Route>
// Or to some not found component
<Redirect to="/r1/A" />
</Switch>
);
}

I'm using Switch with my route entries. The problem was that I didn't know how to render a component that I wanted to pass by props to another component.
I added a prop component to my parent component A and in my route I wrote something like this:
<Route path="/r1/hub/A" render={() => <A /> //this render only A
<Route path="/r1/hub/A/B" render={() => <A component={B} /> //this render A with B
In component A I used React.createElement to render component B with others properties that component A has to inject.

Related

React Rerender with radioButtons using redux

I have a question, I will appreciate if you could help me. I was building a simple react movie app, using redux for manage the state. I also use react router dom for manage basic routes.
I have the home page, where I render some radio buttons that when clicked they dispatch an action to filter the movies by a genre.
So my question is that when I clicked in a radio button it made the filter but the radio button it not checked.
Whereas if I put the Home component outside the Route it do it well, the radio button is checked and it filter the movies list.
This is the app component, with the home outside the router working well and the home inside the routing.
<Home props={props}/>
<Switch>
<Route exact path='/home' component={() => <Home props={props}/>} />
<Route path='/genre=:genre' component={detailGenre} />
<Redirect to='/home' />
</Switch>
This is the radio button
const handleRadioButtons = (e) => {
sortMovies(e.target.value)
}
...
<label htmlFor='horror'>Horror</label>
<input onChange={handleRadioButtons} name='filmtype' value='horror' type='radio' id='horror' />
I'm sure i'm not understanding some behaviour or maybe i'm missing something.
Thanks in advance.
Why is this happening and how can I solve it ?
The component prop tells React Router to generate the component using React.createElement, and if you pass it an inline function it will execute that function and generate the component anew with every single render. In other words, your Home component is being unmounted and remounted with every render. Try this instead, which is the standard way of rendering a component via a route with props:
<Route exact path='/home' render={() => (<Home props={props}/>)} />

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.

How to prevent react router from re-rendering a previously rendered page?

I think I've got some issue with react router. How can I prevent react router from re-rendering a previously rendered page?
I have router code like this:
class App extends React.Component {
render() {
return (
<div>
<NavBar/>
{this.props.children}
</div>
);
}
}
ReactDOM.render(
(
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={Gateway} />
<Route path="home" component={Gateway} />
<Route path="categories" component={Categories} />
</Route>
</Router>
), document.getElementById('my-app')
);
When I first visit the page, it hits index, Gateway component got rendered. Then I click on "categories" link, and Categories component got rendered. Then when I click "home" link again, the component Gateway got re-rendered. Its state got RESET. This is really frustrating, as I could not figure our why its state got reset.
Is there any solution for this?
If there is any state you want to be saved, you should store it somewhere, such as the component's state (or the redux state if you use redux).
In react, you can define the function shouldComponentUpdate() within components in order to force React not to re-render your DOM. But in the case of the react-router, the DOM of the first route is destroyed (not just hidden) and therefore should be re-rendered.

react router parameter loading index route only

So I have my routes defines as follows:
<Route path="/manage" component={Manage}>
<IndexRoute component={Manage}></IndexRoute>
<Route path=":id" component={Edit}></Route>
</Route>
</Route>
Now when I click on a button in my Manage component I call following function:
handleEditClick(e) {
e.preventDefault();
let selectedId= this.state.selectedId;
this.props.router.replace("/manage/" + selectedId);
},
My browser does display me the correct link but my component is not loaded as should. It only renders me the Manage component and not the Edit component.
Am I missing something here?
UPDATE
changing the child route to <Route path="/manage/:id" component={Edit}></Route> also loads me the Manage component
UPDATE 2
if I do not use child routes but in stead create them on the same level, the Edit component does render, but I'd like to use child routes.
On your render() you need a {this.props.children} ... it's where React router knows where to put child Component
In your routes
<Route path="/manage" component={Manage}>
<IndexRoute component={Manage}></IndexRoute>
<Route path=":id" component={Edit}></Route>
</Route>
If you navigate to /manage/:id, React Router renders Manager and Edit components... but where to put Edit?
So
you need to have something like this.
class Manager extends Component {
render(){
return (
<div>
Hello
{this.props.children}
</div>
);
}
}
So React Router knows to put Edit along side when you go to /manage/:id
<div>
Hello
<Edit />
</div>

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