Pass history as props to an event handler - reactjs

I am creating a form which after being filled up and the submit button is clicked should navigate to another component. However, I cant seem to be able to pass history as a prop. I assume I am doing something wrong with the bindings of this but cant figure this out. Thanks.
Here is my App.js
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import {LandingPage} from './landingPage/LandingPage';
import {ReportsPage} from './reportsPage/ReportsPage';
export class App extends React.Component {
render() {
return (
<BrowserRouter >
<Switch>
<Route path="/" exact component={LandingPage}/>
<Route path="/reports"
render={() => <ReportsPage/>}
/>
</Switch>
</BrowserRouter>
);
}
}
Here is my LandingPage.js
export class LandingPage extends React.Component {
constructor(props){
super(props);
this.state = {
...
this.formAnswersUpdater = this.formAnswersUpdater.bind(this)
}
formAnswersUpdater(e) {
e.preventDefault()
...
history.push("/reports")
}
render() {
return (
<div>
...
<MyForm
onClickOnLastInputsForm={e => this.formAnswersUpdater}
/>
</div>
)
}
And here is where my event is happening. MyForm.js
export class MyForm extends React.Component {
render() {
return(
...
<Route render={({history}) => (
<button className="uk-button uk-button-primary uk-width-1-1#s"
style={{backgroundColor:'#41f44f',
color:'#666', margin: 0}}
id='buttonSliders'
/*if done here it works*/
/*onClick={() => {history.push("/reports")}}*/
/*However if passed to the event handler it does not*/
onClick={() => {this.props.onClickOnLastInputsForm}}
>
ClickMe!
</button>
)}/>
)
My react-router-dom version is: "^4.2.2"

Ok, here is how I handled the issue.
Instead of exporting the LandingPage component, I wrapped it in withRouter function and then exported it.
class LandingPage extends React.Component {
constructor(props){
super(props);
this.state = {
...
this.formAnswersUpdater = this.formAnswersUpdater.bind(this)
}
formAnswersUpdater(e) {
e.preventDefault()
...
//added this.props. here
this.props.history.push("/reports")
}
render() {
return (
<div>
...
<MyForm
onClickOnLastInputsForm={e => this.formAnswersUpdater}
/>
</div>
)
}
// wrapped it with withRouter
export default withRouter(LandingPage)
And then in MyForm component I just called the eventHandler.
export class MyForm extends React.Component {
render() {
return(
...
<button className="uk-button uk-button-primary uk-width-1-1#s"
style={{backgroundColor:'#41f44f',
color:'#666', margin: 0}}
id='buttonSliders'
onClick={this.props.onClickOnLastInputsForm()}
>
ClickMe!
</button>
)

I don't know if this will solve all of your problems, but I see something important missing in your LandingPage render function
onClickOnLastInputsForm={e => this.formAnswersUpdater}
You forgot to pass in your argument: e
onClickOnLastInputsForm={e => this.formAnswersUpdater(e)}
Make sure you add this on MyForm as well. You forgot it there as well. Do you get any other errors after fixing those?
edit: After some inspection of the docs, it looks like the typical use of react-router-dom has a different pattern than what you have. Most common pattern with route handling is to have a root set of routes and use Link from the npm package to navigate. I only see history and its derivatives in the docs for react-router. I'm aware that react-router-dom is a derivative of react-router, but I think if you follow the normal patterns for React, it'll be a lot easier for you in the future when you're debugging.
A nice article on it. It even has a sample app using it: https://medium.com/#pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf

Related

Call class method from another component

I'm stuck with React what's a new programming environment for me. So probably I use wrong names for certain objects.
I want to call a method in a class in file 'App.jsx' from a component in file 'Navbar.jsx'. This is (part of) my code so far:
App.jsx:
import React, {} from "react";
import { Navbar, Home, Footer, Documentation } from "./components";
class App extends React.Component {
constructor(props)
{
super(props);
this.state = { mainComponent: 'Home' };
}
getClick = () => {
console.log('test');
}
render() {
return (
<div className="min-h-screen gradient-bg-welcome">
<Navbar getClick={this.getClick}/>
<Home/>
<Footer/>
</div>
);
}
}
export default App;
Navbar.jxs:
...
const Navbar = () => {
...
return (
...
<div ... onclick={() => (call getClick in App.jsx here)}>
...
</div>
);
}
export default Navbar;
I searched the internet and tried some code from several examples, but I probably miss something simple. I can't figure out how to call getClick from Navbar.jsx.
I also tried using states, which should be a better option from what I read, but that also didn't work out for me.
My goal:
In App.jsx I have this:
<Navbar/>
<Home/>
<Footer/>
I want, from the Navbar where I have some link texts, to reload/change the component between Navbar and Footer for another component based on what link I click in the Navbar.
Try this
const Navbar = (props) => {
...
return (
...
<div ... onclick={() => props.getClick()}>
...
</div>
);
}
export default Navbar;
What we did here is simple, we simply passed the function as a prop from the parent. And used the passed props to access and call the function.

How to navigate to other page using react router

I have a onClick function to navigate to other page. I tried this.props.history.push("/SecondPage/ID/") and some examples but nothing worked out.
I have the component like this:
export class MainPage extends Component {
constructor(props) {
super(props);
}
render(){
return (
<div id="main" onClick={this.NavigatetoOtherPage.bind(this)}>
)
}
NavigatetoOtherPage(){
let ID = this.props.ID; // I need to pass the ID as a parameter.
//Here I need to navigate to other page using. I can use window.location.href but I need to use react router.
}
}
export default connect(state => {
return {
ID: state.Reducer.ID,
};
})(MainPage)
My app.js file like this
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Route exact path='/' component={MainPage}/>
<Route path='/SecondPage/:ID/' component = {SecondPage} />
</Provider>
);
}
}
My index.js page like this
export function renderPage() {
ReactDOM.render(
<Router>
<App />
</Router>
, document.getElementById('root'));
}
renderPage();
How can I navigate to second page without window.location.href
You can use the useHistory hook or the Link component given you are using react-router-dom
import React from "react";
import { useHistory, Link } from "react-router-dom";
// Then in your component
const MainPage = (props) => {
/**
* hooks
*/
const history = useHistory();
/**
* function
*/
const handleNavigation = () => {
let ID = props.ID; // I need to pass the ID as a parameter.
history.push(`/dashboard/${ID}`)
}
return (
<button id="main" onClick={() => history.push("/")}> Go to / </button>
<button id="main" onClick={() => handleNavigation()}> Go to dynamic page
</button>
<Link to={`/dashboard/${props.ID}`} className="some-styling">
Using Link
</Link>
);
};
// I have merged both implementations
export default MainPage;
// Edited: Based on the comment, the issue is "The history is not coming in the props."
// Then you could use `withRouter` HOC, and then there will be
// the `history` object in the wrapped component's props.
import {withRouter} from 'react-router-dom';
class MainPage extends React.Component {
render(){
console.log(this.props.history) // history object
return(<div />)
}
}
export default withRouter(MainPage)`
Wrote down a small sandbox. I guess this is what you are trying to achieve.
https://codesandbox.io/s/practical-tereshkova-ilbig?file=/src/App.js

React-router custom prop not passing to component. ternary operator not working correctly

In React i have my App.js page where i keep my states. I'm importing user1.js component to App.js, and in user1.js component i have a link button that takes me to path /user2.
When i click the button, React will set state property called testValue to true and in user2.js page ternary operator should choose the first value - test works because of that. But for some reason it does not work.
Any help?
APP.JS
import React, { Component } from 'react';
import './App.css';
import User1 from './components/user1';
class App extends Component {
constructor(props){
super(props);
this.state = {
testValue:false
};
}
change = () => {
this.setState({
testValue:true
},() => {
console.log(this.state.testValue)
});
}
render() {
return (
<div className="App">
<User1 change={this.change}/>
</div>
);
}
}
export default App;
USER1.JS
import React from 'react';
import { BrowserRouter, Route, Switch, Link } from 'react-router-dom';
import User2 from './user2.js';
const User1 = (props) => {
return(
<BrowserRouter>
<div>
<Link to ="/user2">
<button onClick={props.change}>Next page</button>
</Link>
<Switch>
<Route path="/user2" exact component={User2}/>
</Switch>
</div>
</BrowserRouter>
); // end of return
};
export default User1;
USER2.JS
import React from 'react';
const User2 = (props) => {
console.log(props)
return(
<div>
{props.testValue ?
<p>test works</p>
:
<p>test does not work</p>
}
</div>
);
};
export default User2;
This is what i expected - test works
This is what i got - test does not work
You want to pass a custom property through to a component rendered via a route. Recommended way to do that is to use the render method.
<Route path="/user2" exact render={(props) => <User2 {...props} testValue={true} />} />
I think a valid inquiry here would be what are you wanting to pass through as an extra prop? whats the use case here? You may be trying to pass data in a way you shouldn't (context would be nice :D).

Passing an event method between siblings component in ReactJS

I am practicing in ReactJS and I have a trouble in passing a method between 2 sibling component. I have created React app which has 3 component: MainPage is the parent, FirstPage and SecondPage are two children. In FirstPage component there is a header with some text and SecondPage component has a button. My main goal is to pass the change-header method I defined in FirstPage, through MainPage component, to SecondPage component, so that when I click on the button that event method is fired.
I follow this tutorial https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17 to build my app. I also use react-router-dom in MainPage to display two page: one for FirstPage, another for SecondPage
Here is my FirstPage component:
import React from 'react'
class FirstPage extends React.Component{
constructor(props){
super(props)
this.state = {
msg: 'First page'
}
}
componentDidMount(){
this.props.callBack(this.changeText.bind(this))
}
render(){
return(
<div className = "first">
<h2>{this.state.msg}</h2>
</div>
)
}
changeText(){
{/* event method I defined to change the header text*/}
this.setState({msg: 'Text changed !!'})
this.props.history.push('/first')
}
}
export default FirstPage
and MainPage component:
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import React from 'react'
import FirstPage from '../component/FirstPage'
import SecondPage from '../component/SecondPage'
class MainPage extends React.Component{
constructor(props){
super(props);
this.state = {
func : null
}
}
myCallBack(callFunc){
this.setState({func: callFunc})
}
render(){
return(
<div className = "main">
<Router>
<Switch>
<Route path = "/first" render = {(props) => <FirstPage {...props} callBack = {this.myCallBack.bind(this)} />} />
<Route path = "/second" render = {(props) => <SecondPage {...props} myFunc = {this.state.func}/>} />
</Switch>
</Router>
</div>
)
}
}
export default MainPage
Follow the tutorial, I defined the property func inside MainPage state to store the event method from FirstPage. The myCallBack method is used to change the property of state. And I pass that method to the FirstPage by using callBack = {this.myCallBack.bind(this)}. So in the FirstPage, when the this.props.callBack(this.changeText.bind(this)) called, the event method will be stored into MainPage state
And finally my SecondPage commponent:
import React from 'react'
class SecondPage extends React.Component{
render(){
return(
<div className = "second">
<h2>Second page</h2>
<button onClick = {this.props.myFunc}> Click here to change</button>
</div>
)
}
}
export default SecondPage
App.js :
import React from 'react'
import MainPage from './component/MainPage'
function App() {
return (
<div className = "App">
<MainPage/>
</div>
);
}
export default App;
I simply pass the this.state.func that store my event to SecondPage through props. And I think this should be work: when I click the button, React will redirect to the 'FirstPage' and change the header field. But in fact when I clicked, nothing happen. Can anyone show me which part I did wrong ?
Hi Quang.
In order to do this, that is a possible approach you can follow:
For any shared data between two siblings of a parent component, we basically put that shared data in the closest parent, which in this case MainPage
Further:
The content you want show in FirstPage and change by SecondPage should exist in the state of the parent component MainPage
Pass the content to the FirstPage as a prop, such like content={this.state.content}
The function that changes the content changeText should be inside MainPage because it will be changing the specific state that is sent as a prop to the FirstPage
Function, when invoked by SecondPage should be changing the state of the MainPage, which is passed to the FirstPage as the content of the header.
Solution:
- FirstPage:
// Re-write as a functional component, because we won't be using lifecycle methods, thus, no need for it to be class component.
import React from 'react'
const FirstPage = (props) => (
<div className = "first">
<h2>{props.msg}</h2>
</div>
);
export default FirstPage
- SecondPage:
// Re-write as a functional component, because we won't be using lifecycle methods, thus, no need for it to be class component.
import React from 'react'
const SecondPage = (props) => (
<div className = "second">
<h2>Second page</h2>
<button onClick = {props.changeMsg}> Click here to change</button>
</div>
)
export default SecondPage
- MainPage:
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import React from "react";
import FirstPage from "../component/FirstPage";
import SecondPage from "../component/SecondPage";
class MainPage extends React.Component {
constructor(props) {
super(props);
this.state = {
msg: '', //added msg that is shown in FirstPage
};
}
// Added the function changeMsg to modify msg value in the state by the button onClick event from SecondPage.
changeMsg(){
{/* event method I defined to change the header text*/}
this.setState({ msg: 'Text changed !!' })
this.props.history.push('/first')
}
// Cleared some un-used functions.
// passed msg for FirstPage as a Prop
// passed changeMsg to SecondPage as a Prop
render() {
return (
<div className="main">
<Router>
<Switch>
<Route
path="/first"
render={props => (
<FirstPage {...props} msg={this.state.msg} />
)}
/>
<Route
path="/second"
render={props => (
<SecondPage {...props} changeMsg={this.changeMsg.bind(this)} />
)}
/>
</Switch>
</Router>
</div>
);
}
}
export default MainPage;

React Router fetch data on route change for a component without props

In Switch router scenario,how do we fetch fresh data from the server, after coming back to a already mounted component which doesn't have any props. I went through many posts before asking this. All posts suggest use componentWillRecieveUpdate. This life cycle hook will never be called if the component doesn't have any props. I even tried the getDerivedStatefromProps
What is best option to deal with scenarios like this.?
import React from "react";
import { render } from "react-dom";
// import react router
import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom";
class Application extends React.Component {
render() {
return (
<Router>
<div>
<Menu />
<div>
<Switch>
<Route path="/help" component={Help} />
<Route exact path="/" component={OverView} />
<Route component={OverView} />
</Switch>
</div>
</div>
</Router>
);
}
}
class Menu extends React.Component {
render() {
return (
<ul>
<li>
<Link to="/help">Help</Link>
</li>
<li>
<Link to="/">OverView</Link>
</li>
</ul>
);
}
}
class Help extends React.Component {
render() {
return (
<div>
<p>Some help</p>
</div>
);
}
}
class OverView extends React.Component {
constructor(props, context) {
super(props);
this.state = {};
}
getDerivedStatefromProps(prevprops,prevstate, ){
//Even this will also not be called
}
componentWillRecieveUpdate(newprops)
{
//I dont recieve a call to this when come back from help
}
render() {
//can we fetch data here and save in the state
// and re-render. Will this cause any issues
return <div> How to return fetch data</div>;
}
}
render(<Application />, document.getElementById("root"));
I have been digging in your code a little bit, please have a notice that your component is mounting every time, it's not one time mounted as you claimed.
please read the React Router doc to understand more.
You can see it in the Demo
class OverView extends React.Component{
constructor(props,context) {
super(props)
this.state = {}
}
componentDidMount() {
console.log('mounted');
}
render() {
console.log('render');
return(<div> How to return fetch data</div>)
}
}

Resources