Have you encountered The PUSH of UNDEFINED

The PUSH of UNDEFINED

Why this.props.history is undefined and why we cannot call method push of undefined?

Let us understand how this problem arises and what are the ways to solve it.

The Problem

We want to navigate to a different page, we are using react-router but we don’t find history object in props.
The component hierarchy usually looks like this.

Let's say we have a Footer component inside NotHome and on the link click
we want to navigate to AnotherComponent.

<>
<h1>Not Home</>
<Footer />
</>
Footer.jsxconst goToAnotherComponent = () => {
props.history.push("/anotherComponent")
}
const goToHome = () => {
props.history.push("/")
}
<>
<button role="link" onClick={goToAnotherComponent}>
Another Component
</button>
<button role="link" onClick={goToHome}>
Home
</button>
</>

When we click on the button we get the error props.history is undefined.
But we have wrapped it in Browser Router :(.
Let's find out what went wrong.

Why this happens

We get history object from the Route component.

When we pass a component to Route as <Route component={comp}/>
Route calls React.createElement(component, props) and this props contains the history object.

So Home, NotHome, and AnotherComponent have the history object in the props but the Footer component does not, as it is not associated with Route.

The Solution

First Solution:

Pass props to the Footer from each route component that uses Footer.

const NotHome = props => {
<>
<h1>Not Home</>
<Footer props={props} />
</>
}

As NotHome will have history in props, Footer will get the same props and then we can access the history object.

But using this approach we will have to pass props from each of the parent components of Footer which is not very efficient.

Second Solution:

Wrap the Footer component with withRouter. This will provide the Footer component all the functionality that the Route provides and it will get the access to history object from props.

import { withRouter } from 'react-router-dom'const Footer = (props) => {

const goToAnotherComponent = () => {
// props.history is defined now
props.history.push('/anotherComponent')
}
return (
<>
// Footer content
</>
)
}export default withRouter(Footer)

Third Solution

Use useHistory hook to get the history object.
Here we do not get access to history from props, rather we get it as the return value from the hook.

const Footer = () => {
const history = useHistory()
const goToAnotherComponent = () => {
// only history not props.history
history.push('/anotherComponent')
}
return (
<>
// Footer content
</>
)
}

The preferred way

I would not recommend the first way as it involves passing the props from above and we might not use the Footer component in a component that is part of Route.

The third solution is a cleaner way where we use useHistory hook and keep our props clean.

If we want the complete route functionality (props including history, match, location) we can wrap it with withRouter and access each of these from props.

Thanks

Developing expertise in Machine Learning! I share my thoughts here! LinkedIn: https://www.linkedin.com/in/njain9104/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store