Cannot read property 'push' in react - javascript

I'm trying to get a component that will appear after a second.
The component itself simply has only H1.
I use a router so the most correct solution would seem to me to use the history.push but it does not work for me, what am I doing wrong?
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import {withRouter} from 'react-router-dom'
import './App.css';
import Hello from './comp/Hello';
class App extends Component {
render() {
return (
<Router>
<div className="App">
</div>
<Route path="/hello" component={Hello} />
</Router>
);
}
componentWillMount() {
this.hello()
}
hello(){
setTimeout(() => {
this.props.history.push('/hello')
}, 1000);
}
}
export default App;
===========================the Component========================
import React, { Component } from 'react';
class App extends Component {
render() {
return (
<div className="App">
<h1>hi</h1>
</div>
);
}
}
export default App;

i think you need to wrap you component with withRouter hook .
try like below,
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import {withRouter} from 'react-router-dom'
import './App.css';
import Hello from './comp/Hello';
class App extends Component {
render() {
return (
<Router>
<div className="App">
</div>
<Route path="/hello" component={Hello} />
</Router>
);
}
componentWillMount() {
this.hello()
}
hello(){
setTimeout(() => {
this.props.history.push('/hello')
}, 1000);
}
}
export default withRouter(App);

It is hard to see exactly what is going on, usually only child components suffer from these kinds of errors in my experience, because the props.history is not being passed on. The Apps component should in most cases be the main component though.
Regardless, in the meantime I suggest you try this approach:
import { BrowserRouter as Router, Route, Link, Redirect } from "react-router-dom";
...
hello(){
setTimeout(() => {
return <Redirect to={{
pathname: "/hello",
state: {
optionalStateToPass,
},
}} />;
}, 1000);
}
...

For a component to receive the history prop, you need 2 things:
Router must be at the top level, above all calls to Route, Link, and withRouter
The component that needs the prop history must be rendered by a Route, e.g. <Route component={ComponentThatNeedsHistoryProp} ...> OR wrap it with withRouter.
In your case, it seems you cannot rely on the route props to be passed, so we need to use withRouter.
It should look like this (imports not shown):
// App.js
class App = () => (
<div className="App">
<Router>
<Hello />
</Router>
</div>
)
// Hello.js
class Hello extends Component {
render() {
return <h1>hi</h1>
}
componentWillMount() {
this.hello()
}
hello(){
setTimeout(() => {
this.props.history.push('/hello')
}, 1000);
}
}
export default withRouter(Hello);

Related

Unable to console.log props using Link

I am trying to make a single web application. Basically, I am trying to use the ReactRouter to display what is passed as a Route Parameter. However, I am unable to do that. To check if somethings wrong, I decided to console.log out this.props.match, still nothing shows up. Could someone explain what the problem is? And a possible get around?
My code is-
import React from 'react';
export default class Post extends React.Component {
state = {
id: null
}
componentDidMount(props) {
console.log(this.props.match);
}
render = () => {
return (<div>Hello WOrld</div>)
}
}
The App.js file:
import React, { Fragment, Component } from 'react';
import Navbar from './components/Navbar';
import Home from './components/Home';
import Contact from './components/Contact';
import About from './components/About'
import Post from './components/Post';
import { BrowserRouter, Route } from 'react-router-dom';
class App extends Component {
render = () => {
return (
<BrowserRouter>
<div className="App">
<Navbar />
<Route exact path="/" component={Home} />
<Route path="/contact" component={Contact} />
<Route path="/about" component={About} />
<Route path="/:post-id" component = {Post} />
</div>
</BrowserRouter>
);
}
}
export default App;
I just ran your code on my end, it looks like the problem is using /:post-id. I changed that to /:pid and it worked. I got the below object when I console log this.props.match
{
"path":"/:pid",
"url":"/1",
"isExact":true,
"params":
{
"pid":"1"
}
}
I hope this helps.
You have to load the component with router
try this
import { withRouter } from 'react-router-dom';
class Post extends React.Component {
state = {
id: null
}
componentDidMount(props) {
console.log(this.props.match);
}
render = () => {
return (<div>Hello WOrld</div>)
}
}
export default withRouter(Post);

Faced TypeError: render is not a function when using Context API

I am new to React ans was learning Context API and during the use of it I faced this error TypeError: render is not a function. I also found the this answer React Context: TypeError: render is not a function in the platform which is close to my problem but no result. Here is the code I am using:
import React, { Component } from "react";
import MyContext from "../../Containers/Context/Context";
class Track extends Component {
render() {
return (
<MyContext>
{value => {
return <div>{value.heading}</div>;
}}
</MyContext>
);
}
}
export default Track;
import React, { Component } from "react";
const Context = React.createContext();
export class MyContext extends Component {
state = { track_list: [], heading: "Top Ten Tracks" };
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
);
}
}
export default MyContext = Context.Consumer;
import React, { Component, Fragment } from "react";
import "./App.css";
import Header from "../src/Components/Header/Header";
import Search from "../src/Components/Search/Search";
import Tracks from "../src/Components/Tracks/Tracks";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import NotFound from "./Components/NotFound/NotFound";
import MyContext from "./Containers/Context/Context";
class App extends Component {
render() {
return (
<MyContext>
<Router>
<Fragment>
<Header />
<div className="container">
<Search />
<Switch>
<Route exact path="/" component={Tracks} />
<Route component={NotFound} />
</Switch>
</div>
</Fragment>
</Router>
</MyContext>
);
}
}
export default App;
Your export and import statements are problematic.
first you export class MyContext then you immediately overwrite MyContext with Context.Consumer.
Fix your export statements and then fix your imports. import the Context.Consumer in file Track, and import the Context.Provider in file App
Containers/Context/Context.js
import React, { Component } from "react";
const Context = React.createContext();
class MyContextProvider extends Component {
state = { track_list: [], heading: "Top Ten Tracks" };
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
);
}
}
const MyContextConsumer = Context.Consumer;
export {MyContextProvider,MyContextConsumer};
Track.js
import React, { Component } from "react";
import {MyContextConsumer} from "../../Containers/Context/Context";
class Track extends Component {
render() {
return (
<MyContextConsumer>
{value => {
return <div>{value.heading}</div>;
}}
</MyContextConsumer>
);
}
}
export default Track;
App.js
import React, { Component, Fragment } from "react";
import "./App.css";
import Header from "../src/Components/Header/Header";
import Search from "../src/Components/Search/Search";
import Tracks from "../src/Components/Tracks/Tracks";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import NotFound from "./Components/NotFound/NotFound";
import {MyContextProvider} from "./Containers/Context/Context";
class App extends Component {
render() {
return (
<MyContextProvider>
<Router>
<Fragment>
<Header />
<div className="container">
<Search />
<Switch>
<Route exact path="/" component={Tracks} />
<Route component={NotFound} />
</Switch>
</div>
</Fragment>
</Router>
</MyContextProvider>
);
}
}
export default App;

Redirecting using React Router shows blank page

I'm trying to build a simple example project where the user is redirected to the 'contact' page upon clicking a button, using React. I'm trying to achieve this by setting the value of a state property. When I run the code I have, it does change the browser address bar URL to that of the contact page, but does not seem to actually load the component - I get a blank page instead. If I manually navigate to that URL (http://localhost:3000/contact) I can see the contents.
Here are my App.js and Contact.js files -
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route, Link, Redirect } from 'react-router-dom';
import Contact from './Contact';
class App extends Component {
state = {
redirect: false
}
setRedirect = () => {
this.setState({
redirect: true
})
}
renderRedirect = () => {
if (this.state.redirect) {
return <Redirect to='/contact' />
}
}
render() {
return (
<Router>
<Switch>
<Route exact path='/contact' component={Contact} />
</Switch>
<div>
{this.renderRedirect()}
<button onClick={this.setRedirect}>Redirect</button>
</div>
</Router>
)
}
}
export default App;
Contact.js
import React, { Component } from 'react';
class Contact extends Component {
render() {
return (
<div>
<h2>Contact Me</h2>
<input type="text"></input>
</div>
);
}
}
export default Contact;
Using state isn't really a requirement for me, so other (preferably simpler) methods of redirection would be appreciated too.
Since your button is nothing more than a link, you could replace it with:
<Link to="/contact">Redirect</Link>
There are many alternatives though, you could for example look into BrowserRouter's browserHistory:
import { browserHistory } from 'react-router'
browserHistory.push("/contact")
Or perhaps this.props.history.push("/contact").
There are pros and cons to every method, you'll have to look into each and see which you prefer.
I got here for a similiar situation. It's possible use withRouter (https://reactrouter.com/web/api/withRouter) to handle that.
This example was tested with "react": "^16.13.1","react-router-dom": "^5.2.0" and "history": "^5.0.0" into "dependecies" sections in package.json file.
In App.js I have the BrowserRouter (usually people import BrowserRouter as Router, I prefer work with original names) with Home and Contact.
App.js
import React, { Component } from "react";
import {
BrowserRouter,
Switch,
Route,
} from "react-router-dom";
import Home from "./pages/Home";
import Contact from "./pages/Contact";
class App extends Component
{
// stuff...
render()
{
return (
<BrowserRouter>
<Switch>
<Route path="/contact">
<Contact />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</BrowserRouter>
);
}
}
export default App;
ASIDE 1: The Route with path="/contact" is placed before path="/" because Switch render the first match, so put Home at the end. If you have path="/something" and path="/something/:id" place the more specific route (with /:id in this case) before. (https://reactrouter.com/web/api/Switch)
ASIDE 2: I'm using class component but I believe (I didn't test it) a functional component will also work.
In Home.js and Contact.js I use withRouter associated with export keyword. This makes Home and Contact components receive the history object of BrowserRouter via props. Use method push() to add "/contact" and "/" to the history stack. (https://reactrouter.com/web/api/history).
Home.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const Home = ( props ) =>
{
return (
<div>
Home!
<button
onClick={ () => props.history.push( "/contact" ) }
>
Get in Touch
<button>
</div>
);
}
export default withRouter( Home );
Contact.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const Contact = ( props ) =>
{
return (
<div>
Contact!
<button
onClick={ () => props.history.push( "/" ) }
>
Go Home
<button>
</div>
);
}
export default withRouter( Contact );
Particularly, I'm using also in a BackButton component with goBack() to navigate backwards:
BackButton.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const BackButton = ( props ) =>
{
return (
<button
onClick={ () => props.history.goBack() }
>
Go back
<button>
);
}
export default withRouter( BackButton );
So I could modify the Contact to:
Contact.js (with BackButton)
import React from "react";
import BackButton from "../components/BackButton";
export const Contact = ( props ) =>
{
return (
<div>
Contact!
<BackButton />
</div>
);
}
export default Contact; // now I'm not using history in this file.
// the navigation responsability is inside BackButton component.
Above was the best solution for me. Other possible solutions are:
useHistory Hook (https://reactrouter.com/web/api/Hooks)
work with Router instead BrowserRouter - (https://reactrouter.com/web/api/Router)

React JS - Route - Element type is invalid

I'm new to React JS and am trying to implement something similar to the Angular sample application.
I have a table of customers and want to seen the selected customer at the bottom of the table.
I tried the following with react-router-dom:
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import 'bootstrap/dist/css/bootstrap.css';
ReactDOM.render((<BrowserRouter><App /></BrowserRouter>), document.getElementById('root'));
registerServiceWorker();
// App.js
import React, { Component } from 'react';
import { Route } from 'react-router-dom/Route';
import Customers from './components/customers';
import Customer from './components/customer';
export default class App extends Component {
state = {
};
render() {
return (
<React.Fragment>
<Customers />
<Route path={`/customer/:id`} component={Customer} />
</React.Fragment>
);
}
}
// customers.jsx
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
export default class Customers extends Component {
state = {
customers: []
};
render() {
return (
<React.Fragment>
<header className="jumbotron"><h1>Customer List</h1></header>
<div className="container">
<table className="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
{this.state.customers.map(c => (<tr key={c.id}><td><Link to={`/customer/${c.id}`}>{c.name}</Link></td><td>{c.address}</td></tr>))}
</tbody>
</table>
<hr />
</div>
<footer className="footer">© 2018</footer>
</React.Fragment>
);
}
async componentDidMount() {
const result = await fetch('http://api.com/customers');
const customers = await result.json();
this.setState({ customers });
console.log(this.state.customers);
}
}
// customer.jsx
import React, { Component } from 'react';
export default class Customer extends Component {
render() {
return (<p>Customer</p>);
};
}
The line in App.js that adds the Route (Route path={/customer/:id} component={Customer}) is causing the error. If I remove that line I can see the table of customers but as soon as I add this line, then I get that error message.
Did I miss something on how this router works?
Thank you.
UPDATE
Event changing App.js to this very simple version causes the error
import React, { Component } from 'react';
import { Route } from 'react-router-dom/Route';
export default class App extends Component {
state = {
};
render() {
return (
<div>
<Route exact path='/' render={() => (<h1>Hello</h1>)} />
<Route exact path='/customer' render={() => (<h1>Customer</h1>)} />
</div>
);
}
}
The full error message is:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
change this:
import {Route} from "react-router-dom/Route";
to this:
import Route from "react-router-dom/Route";
Route is a default export when you access it directly: "react-router-dom/Route"
You can use named exports when you import Route from base package
import {Route} from "react-router-dom";
But don't mix the two.
I feel the issue is with back ticks in path. Can you try with this
render() {
return (
<React.Fragment>
<Customers />
<Route path='/customer/:id' component={Customer} />
</React.Fragment>
);
}
OR
render() {
return (
<React.Fragment>
<Customers />
<Route path={'/customer/:id'} component={Customer} />
</React.Fragment>
);
}
Looks like you have a syntax error in the Customer component's render() method which will be causing issue when you attempt to use/render this.
Try the following fix:
export default class Customer extends Component {
render() {
return (<p> Customer</p>); // <-- remove the whitespace in
// closing tag </p> like so
};
}

React-router-dom and Redux not rerendered the component

EDIT : I found the solution, I forget switch component in my routes.js
I have created a route like that : /post/:id
my routes.js look like :
import React, { Component } from "react";
import { BrowserRouter, Route } from "react-router-dom";
import Posts from "./components/Posts";
import Post from "./components/Post";
class Routes extends Component {
render() {
return (
<div>
<BrowserRouter>
<div>
<Route exact path="/" component={Posts} />
<Route path="/post/:id" component={Post} />
</div>
</BrowserRouter>
</div>
);
}
}
export default Routes;
At my /route I listed all my posts, and when I click on one of them I update the url http://localhost:3000/post/1 for example.
PS : I tried to use withRouter function as it says in the official document of react-router, but this case not work in my case.
The file in question is post.js it look like :
import React, { Component } from "react";
import { active_post } from "../actions/index";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
class Post extends Component {
componentWillMount() {
this.props.active_post(this.props.match.params.id);
}
render() {
console.log(this.props.post);
return (
<div>
<h1>Detail d'un post</h1>
<p>{this.props.match.params.id}</p>
</div>
);
}
}
function mapStateToProps(state) {
return {
post: state.activePost
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ active_post }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Post);
So for example, if I enter manually in the URL bar http://localhost:3000/post/3 for example I have the component and the console.log() with the post when I need.
So please if you have a solution for this case I take this.
Thanks you :)
You should try adding a Switch from react-router-dom around your routes.
<BrowserRouter>
<div>
<Switch>
<Route exact path="/" component={Posts} />
<Route path="/post/:id" component={Post} />
</Switch>
</div>
</BrowserRouter>
Don't forget to include the Switch module from react-router-dom in your component.
import { Route, Switch } from 'react-router-dom';

Categories

Resources