EiteWald
EiteWald

Reputation: 45

Reactjs - how to pass props to Route?

I’m learning React Navigation using React-Router-Dom. I have created a simple app to illustrate the problem:

Inside App.js I have a Route, that points to the url “/” and loads the functional Component DataSource.js.

Inside DataSource.js I have a state with the variable name:”John”. There is also a buttonwith the onclick pointing to a class method that’s supposed to load a stateless component named ShowData.js using Route.

ShowData.js receives props.name.

What I want to do is: when the button in DataSource.js is clicked, the url changes to “/showdata”, the ShowData.js is loaded and displays the props.name received by DataSource.js, and DataSource.js goes away.

App.js

import './App.css';
import {Route} from 'react-router-dom'
import DataSource from './containers/DataSource'
function App() {
  return (
    <div className="App">
      <Route path='/' component={DataSource}/>
    </div>
  );
}

export default App;

DataSource.js

import React, { Component } from 'react';
import ShowData from '../components/ShowData'
import {Route} from 'react-router-dom'
class DataSource extends Component{
    state={
        name:' John',
    }

    showDataHandler = ()=>{
        <Route path='/showdata' render={()=><ShowData name={this.state.name}/>}/>
    }

    render(){
        return(
            <div>
                 <button onClick={this.showDataHandler}>Go!</button>                
            </div>           
        )
    }
}

export default DataSource;

ShowData.js

import React from 'react';

const showData = props =>{
    return (
        <div>            
            <p>{props.name}</p>
        </div>
    )
}

export default showData;

I have tried the following, but, even though the url does change to '/showdata', the DataSource component is the only thing being rendered to the screen:

DataSource.js

showDataHandler = ()=>{
        this.props.history.push('/showdata')
    }

    render(){
        return(
            <div>
                 <button onClick={this.showDataHandler}>Go!</button>    
                 <Route path='/showdata' render={()=>{<ShowData name={this.state.name}/>}}/>     
            </div>           
        )
    }

I also tried the following but nothing changes when the button is clicked:

DataSource.js

 showDataHandler = ()=>{
        <Route path='/showdata' render={()=>{<ShowData name={this.state.name}/>}}/>
    }

    render(){
        return(
            <div>
                 <button onClick={this.showDataHandler}>Go!</button> 
            </div>           
        )
    }

How can I use a nested Route inside DataSource.js to pass a prop to another component?

Thanks.

EDIT: As user Sadequs Haque so kindly pointed out, it is possible to retrieve the props when you pass that prop through the url, like '/showdata/John', but that's not what I'd like to do: I'd like that the url was just '/showdata/'.

He also points out that it is possible to render either DataSource or ShowData conditionally, but that will not change the url from '/' to '/showdata'.

Upvotes: 2

Views: 2260

Answers (2)

EiteWald
EiteWald

Reputation: 45

I figured it out. At least, one way of doing it, anyway.

First, I added a route to the ShowData component inside App.js, so that ShowData could get access to the router props. I also included exact to DataSource route, so it wouldn't be displayed when ShowData is rendered.

App.js

import './App.css';
import {Route} from 'react-router-dom'
import DataSource from './containers/DataSource'
import ShowData from './components/ShowData'
function App() {
  return (
    <div className="App">
       <Route exact path='/' component={DataSource}/>
      {/* 1. add Route to ShowData */}
      <Route path='/showdata' component={ShowData}/>
    </div>
  );
}

export default App;

Inside DataSource, I modified the showDataHandler method to push the url I wanted, AND added a query param to it.

DataSource.js

import React, { Component } from 'react';

class DataSource extends Component{
    state={
        name:' John',
    }

    showDataHandler = ()=>{
        this.props.history.push({
          pathname:'/showdata',
          query:this.state.name
        })
    }

    render(){
        return(
            <div>
                 <button onClick={this.showDataHandler}>Go!</button>                
            </div>           
        )
    }
}

export default DataSource;

And, finally, I modified ShowData to be a Class, so I could use state and have access to ComponentDidMount (I guess is also possible to use hooks here, if you don't want to change it to a Class).

Inside ComponentDidMount, I get the query param and update the state.

ShowData.js

import React, { Component } from 'react';

class ShowData extends Component{

  state={
    name:null
  }

  componentDidMount(){
    this.setState({name:this.props.location.query})
  }

  render(){
    
    return (
      <div>            
          <p>{this.state.name}</p>
      </div>
    )
  }    
}

export default ShowData;

Now, when I click the button, the url changes to '/showdata' (and only '/showdata') and the prop name is displayed.

Hope this helps someone. Thanks.

Upvotes: 0

Sadequs Haque
Sadequs Haque

Reputation: 159

There were multiple issues to solve and this solution worked as you wanted.

App.js should have all the routes. I used Route params to pass the props to ShowData. So, /showdata/value would pass value as params to ShowData and render ShowData. And then wrapped the Routes with BrowserRouter. And then used exact route to point / to DataSource because otherwise DataSource would still get rendered as /showdata/:name has /

DataSource.js will simply Link the button to the appropriate Route. You would populate DataSourceValue with the appropriate value.

ShowData.js would read and display value from the router prop. I figured out the object structure of the router params from a console.log() of the props object. It ended up being props.match.params

App.js

import { BrowserRouter as Router, Route } from "react-router-dom";
import DataSource from "./DataSource";
import ShowData from "./ShowData";
function App() {
  return (
    <div className="App">
      <Router>
        <Route exact path="/" component={DataSource} />
        <Route path="/showdata/:name" component={ShowData} />
      </Router>
    </div>
  );
}

export default App;

DataSource.js

import React, { Component } from "react";
import ShowData from "./ShowData";
class DataSource extends Component {
  state = {
    name: " John",
    clicked: false
  };

  render() {
    if (!this.state.clicked)
      return (
        <button
          onClick={() => {
            this.setState({ name: "John", clicked: true });
            console.log(this.state.clicked);
          }}
        >
          Go!
        </button>
      );
    else {
      return <ShowData name={this.state.name} />;
    }
  }
}

export default DataSource;

ShowData.js

import React from "react";

const ShowData = (props) => {
  console.log(props);

  return (
    <div>
      <p>{props.name}</p>
    </div>
  );
};

export default ShowData;

Here is my scripts on CodeSandbox. https://codesandbox.io/s/zen-hodgkin-yfjs6?fontsize=14&hidenavigation=1&theme=dark

Upvotes: 1

Related Questions