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

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

Related

How can I display nested component with React router?

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.

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

Passing props to component inside router

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

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>

Resources