React react-router-dom pass props to component - javascript

I need to pass props to component using router.
Here's my code:
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import AppBarTop from './appbar/AppBarTop';
import Login from '../pages/login/Login';
import {BrowserRouter as Router, Route} from 'react-router-dom';
class App extends Component {
render() {
const { isAuthenticated } = this.props;
return (
<Router>
<div>
<AppBarTop isAuthenticated={isAuthenticated} />
<div className="content">
<Route path="/login" isAuthenticated={isAuthenticated} component={Login} />
</div>
</div>
</Router>
);
}
}
As you can see, isAuthenticated the prop i want to pass to Login component.
class Login extends Component {
constructor(props) {
super(props);
console.log(props);
}
render() {
return (
<LoginForm />
);
}
}
export default connect(null) (Login);
When i log the props the isAuthenticated prop is not there. What i'm doing wrong? How can i pass the prop correctly?
I followed the docs and also other discussions. From my understanding it should work.
The version of react-router and react-router-dom is 4.0.0

Pass it like this:
<Route
path="/login"
render={(props) => <Login {...props} isAuthenticated={isAuthenticated}/>}
/>
It should be available by this.props.isAuthenticated in Login Component.
Reason of {...props}:
If we don't write this then only isAuthenticated will get passed to Login component, all other values that router passes to component, will not be available inside Login component. When we write {...props} then we are passing all the values with one extra value.
And instead of using component with router use render method.
As per DOC:
Component:
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 attribute, 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.
Render:
This allows for convenient inline rendering and wrapping without the
undesired remounting.

Related

How can I avoid unnecessary re rendering of an HOC component, when the parameter component renders in ReactJS with react router

I am using HOC(Layout component here) to wrap my custom component. The Layout component contains Header and sidebar. On clicking link, it will be rendering the respective component. But my problem is that with every route, my HOC gets rendered as route target component is wrapped in this HOC. How can I make my HOC render only once.
Example Snippet.
App.js
<Router>
<Switch>
<PrivateRoute path="routeOne" component={RouteOne}/>
<PrivateRoute path="routeTwo" component={RouteTwo}/>
</Switch>
</Router>
RouteOne.js
import React from "react"
import Layout from "/hoc"
const RouteOne = () =>{
return({..jsx..})
}
export default Layout(RouteOne)
Layout.js
const Layout(WrappedComponent) => {
const userDetails = useSelector(state);
useEffect(()=>{
dispatch(fetchSomething())
},[dispatch])
return ( <HeaderNavbarUILayout header={<Header
username={userDetails.userName}>
content={<WrappedComponent/>);
}
export default Layout
I want to render my HOC component only once. How can I do that?
EDIT:
I would follow this pattern https://simonsmith.io/reusing-layouts-in-react-router-4
const DefaultLayout = ({component: Component, ...rest}) => {
return (
<Route {...rest} render={matchProps => (
<div className="DefaultLayout">
<div className="Header">Header</div>
<Component {...matchProps} />
<div className="Footer">Footer</div>
</div>
)} />
)
};
then where you would normally define routes, replace it with this:
<DefaultLayout path="/" component={SomeComponent} />
I would take a look at the following docs:
how to use useEffect
https://reactjs.org/docs/hooks-reference.html#useeffect
how to implement should component update
https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-shouldcomponentupdate
conditionally firing an effect
```
useEffect(
() => {
...
};
},
[whatever you're watching],
);
```
The hoc being wrapped around is rendered every time and that is expected. But the React's diffing algorithm will render only the changed DOM elements. The problem here was the dispatch is being called every time when the Layout page is rendered and the state gets updated and the particular DOM hence gets updated. This gives an impression of "reload" effect. Dispatching the action conditionally will do the trick. Dispatch only when the state changes.

React/Router/MemoryRouter - how to pass history property and use push() in child component?

I'm building a React app where I do NOT want the URL in the browser to be updated. I am NOT using 'react-router-dom' but only 'react-router' and MemoryRouter (https://reacttraining.com/react-router/web/api/MemoryRouter). The history.push() is available directly in the component statements but I wish to pass the history to children of children of these main components but the property is undefined.
Here is the Router section in main App.js (components Home and ScreeningTool can access this.props.history.push() as expected):
...
import {Route, MemoryRouter} from "react-router";
...
<MemoryRouter>
<Route exact path="/" component={Home} />
<Route path="/screening-tool" component={ScreeningTool} />
</MemoryRouter>
...
Both Home and ScreeningTool both use child component AppLink that generates a 'link' to navigate between Home and ScreeningTool like so (notice I'm passing 'history' as a prop):
Home.js:
...
<AppLink
url="/"
label="Screening Tool"
history={this.props.history}
/>
...
AppLink.js:
...
<div className="pseudo-link">
<span onClick={() => this.props.history.push(this.props.url)}>
{this.props.label}
</span>
</div>
...
The above code works. But there are children components in Home that will create their own AppLinks and also greatgrandchildren. I do NOT want to pass the history property as a component prop from Parent to Child to GrandChild components because this does not seem efficient. I have found the following stackoverflow questions but none of these options are working for me:
this.props.history.push works in some components and not others
react-router getting this.props.location in child components
I tried the newer 'userHistory' as described in the second URL above:
...
import { useHistory } from 'react-router';
...
render() {
let history = useHistory();
return (
<div className="pseudo-link">
<span onClick={() => history.push(this.props.url)}>
{this.props.label}
</span>
</div>
);
}
...
but I get Error: Invalid hook call. Hooks can only be called inside of the body of a function component..
I tried using withRouter as defined here https://reacttraining.com/react-router/web/api/withRouter but I get Error: Invariant failed: You should not use <withRouter(PseudoLink) /> outside a <Router>.
Finally, the accepted answer for this.props.history.push works in some components and not others ends with block of code export default withRouter(connect(mapStateToProps, matchDispatchToProps)(ChildView)); but does not explain where mapStateToProps or matchDispatchToProps comes from?
I'm thinking the issue is that I am using MemoryRouter and not normal/most common Router from 'reacto-router-dom'.
Can anyone help me out?
useHistory is a Hook so it should be used in a functional component, not inside a class based component.
Finally, the accepted answer for this.props.history.push works in some components and not others ends with block of code export default withRouter(connect(mapStateToProps, matchDispatchToProps)(ChildView)); but does not explain where mapStateToProps or matchDispatchToProps comes from?
-If you're not using redux then you can just use
export default withRouter(yourComponentName);
update
I've changed the AppLink component to this and it is working fine
import React from "react";
import "./AppLink.css";
import { useHistory } from 'react-router';
const AppLink = props => {
const history = useHistory();
return (
<div
className="app-link-button"
onClick={() => history.push(props.url)}
>
<span>{props.label}</span>
</div>
);
};
export default AppLink;

React Router structuring - passing functions

I'm quite new to reactjs and was just wondering if there is any easy way to display information from the same component to different routes. In the following code as an example I have just two functions that are just returning divs full of text, and calling them and rendering them right away (in the class or in the router) would just have them be on the same "page".
I've tried passing the ref by props but they always ended up undefined. I figured a state change would be awkward since there is no real "event". I'm using create-react-app, react-routerv4, and react-bootstrap.
In App.js
import React, { Component } from 'react';
import './App.css';
import NavBar from './Components/NavBar/NavBar.js';
import Band from './Components/Text/Band.js';
import { Router, BrowserRouter, Link, Route } from 'react-router-dom';
class App extends Component {
render() {
return(
<div>
<BrowserRouter>
<div className="RenderRouter">
<Route exact path='/' component={NavBar}/>
<Route exact path='/' component={ControlledCarousel}/>
<Route exact path='/' component={Home}/>
//<Route exact path='/Artists/ArtistX' component={Band}/>
<Route exact path='/Artists/Artist1' component={NavBar}/>
<Route exact path='/Artists/Artist1' render={props => <Band band1text = {this.props.band1text} />}/>
<Route exact path='/Artists/Artist2' component={NavBar}/>
<Route exact path='/Artists/Artist2' render={props => <Band band2text = {this.props.band2text} />}/>
</div>
</BrowserRouter>
</div>
);
}
}
export default App;
In Band.js
import React, { Component } from 'react';
import './Band.css';
class Band extends Component {
//Constructor for state change would go here
band1text(props) {
return(
<div id="band1Text" className="BandText">
<h1>"The best riffs!</h1>
</div>
);
};
band2text(props) {
return(
<div id="band2Text" className="BandText">
<p>More info coming soon! Check out the interview!</p>
</div>
);
};
//Create handlers to call functions, and pass reference?
render() {
return(
<div className="BandDescription">
//calling in DOM render object, can't pass props from here?
//{this.props.band1text()} = compiler error
{this.band1text()}
{this.band2text()}
</div>
);
}
}
export default Band;
It would probably be easier to just have separate components and classes for every piece of each route (i.e, BandX.js, CarouselX.js) but that could get verbose and one would have to import many files. I'm using react to build a music player component for the app as well, that's why I'm not just using standard JS.
Try writing something like this in your Band component render:
render() {
return(
<div className="BandDescription">
{this.props.band1text && this.band1text()}
{this.props.band2text && this.band2text()}
</div>
);
}
This way it checks for the prop before running whichever method. If both methods are passed, both functions will return. And you shouldn't need to pass props to those methods. Try writing them as arrow functions so they will be bound band1text = () => { ... }, you will still be able to access this.props.band1text from inside the method.
The props would be undefined because there is no props with bandText being passed down to App component. Routes are nested in App component and this.props.band1Text means you are expecting to read from props passed to App. Try passing band1Text and band2Text as props to App component.
Also to read a props that's not a function just use {this.props.band1Text} in the Band.js component

What effect does React.cloneElement() have on React's reconciliation of the virtual DOM?

I have inherited a code base that makes frequent use of React.cloneElement(this.props.children, {}) in the render() method of container components. Like this:
import React from 'react';
class App extends React.Component {
render() {
<div>
{this.props.children && React.cloneElement(this.props.children, {})}
</div>
}
}
...
import {Route} from 'react-router'
import App from '..';
import SomePureComponent from '..';
<Route
component={App}
path='/'
>
<Route
component={SomePureComponent}
path='/test'
/>
</Route>
Isn't this an anti-pattern? It was my understanding that calling functions like React.cloneElement() will return a new instance on each render, alerting the virtual DOM of new changes it must reconcile, and thus leaving PureComponent ineffective at minimizing re-renders. Am I misunderstanding something about Virtual DOM reconciliation?

React-Route redirect after socket.io

I am trying to redirect to another path after receiving some information from the server, but It seems I can only use within , which I am not able to do.
the flow looks like this
1. client send socket message asking for a path
2. server look up a path, and send it back to client
3. client store the path into redux
4. redirect to the path
How can I achieve this?
Here's two ways you can redirect using react-router-dom, here's an example.
I suggest taking a look at the react-router docs here https://reacttraining.com/react-router/web/guides/philosophy
Method 1:
If you have a component that wraps your whole application use render and pass props for its arguments example.
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Route.md#render-func
//Wrapping component
import {BrowserRouter as Router, Route} from 'react-router-dom'
//Import your store ex:
{store} from '/path/to/store'
import Child from './components/child'
import Navbar from './components/navbar'
import Footer from './components/footer'
class App extends React.Component {
render() {
return(
<Provider store={store}>
<Router>
<div className="root">
<Navbar />
<Route exact path="/child" render={props => <Child {...props}/>}
//More routes.....
<Footer />
</div>
</Router>
</Provider>
)
}
}
//Child component
class SomeComponent extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
socket.on('redirect', url => {
//dispatch your action to update store then redirect
this.props.history.push(url)
})
}
render() {
//some html
}
}
Method 2:
Store the redirect path in the store, and check if not null, you can use middleware, but for this example I'll use a simple if statement inside the component, the drawback using this method is you will need to dispatch an action after the redirect(you may run into some redirect issues if you don't) so it may be useful to wrap the redirect inside a method, and dispatch another action to set it back to null.
import {Redirect} from 'react-router-dom'
class SomeComponent extends React.Component {
render() {
if(this.props.redirectPath) return <Redirect to={this.props.redirectPath} />
else //Render your component
}
}

Categories