Daniele Deltodesco
Daniele Deltodesco

Reputation: 63

Passing function prop to child component (via Link and Route components)

I'm using pure React with a Rails API backend.

I am fetching data from API and storing state in my Trips component. I have a Link component where I am able to pass the state to my NewTrip component, however <Link> does not allow me to pass functions.

I am able to pass functions to NewPage via render method on the Route component located at './routes/Index'.

But how do I pass the function from my Trips component? It's so much easier when passing as props to the component, the Router seems to be in the way!

'routes/Index.js'

export default (
  <Router>
    <Switch>
      <Route path="/" exact component={Home} />
      <Route path="/trips" exact component={Trips} />
      <Route path="/trip" render={(routeProps)=><NewTrip {...routeProps} />}/>
      <Route path="/trip/:id" exact component={Trip} />
      <Route path="/trip/:id/cost" exact component={NewCost} />
      <Route path="/trip/:id/edit" exact component={EditTrip} />
    </Switch>
  </Router>
);

'components/Trips'

class Trips extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      trips: []
    }

    this.addTrip = this.addTrip.bind(this);
  }

  addTrip(trip) {
    const {trips} = this.state;
    trips.push(trip);
    this.setState({ trips: trips});
  }

  render(){
    return(
      <Link
        to={{
          pathname: "/trip",
          state: {trips: trips, onAddTrip={this.addTrip}} // not allowed,
// but I want to pass this function to the 
// Component which is rendered by the Route in Index.js
        }}
        className="btn custom-button">
          Create New Trip
      </Link>
    )
  }
}

Upvotes: 2

Views: 754

Answers (2)

Mahdi Ghajary
Mahdi Ghajary

Reputation: 3253

You can pass functions using state in react router Link.

    <Link
        to={{
          pathname: "/trip",
          state: {trips: trips, onAddTrip: this.addTrip}
        }}
        className="btn custom-button">
          Create New Trip
    </Link>

And then in /trip, you retrieve and use the function like this:

this.props.location.state.addTrip();

Upvotes: 0

Adam Jeliński
Adam Jeliński

Reputation: 1788

I think you should lift state up of the Trips component and have it in your 'routes/Index.js'. (it will need to be a component now, not just an export).

'routes/Index.js'

export default class Routes extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      trips: []
    }

    this.addTrip = this.addTrip.bind(this);
  }

  addTrip(trip) {
    const {trips} = this.state;
    trips.push(trip);
    this.setState({ trips: trips});
  }

  render() {
    return (
      <Router>
        <Switch>
          <Route path="/" exact component={Home} />
          <Route path="/trips" exact component={Trips} />
          <Route path="/trip" render={(routeProps)=>
            <NewTrip addTrip={this.addTrip} trips={this.state.trips} {...routeProps} />
          }/>
          <Route path="/trip/:id" exact render={(routeProps)=>
            <Trip addTrip={this.addTrip} trips={this.state.trips} {...routeProps} />
          }/>
          <Route path="/trip/:id/cost" exact component={NewCost} />
          <Route path="/trip/:id/edit" exact component={EditTrip} />
        </Switch>
      </Router>
    );
  }
}

'components/Trips'

class Trips extends React.Component {
  render() {
    const trips = this.props.trips
    return (
      <Link
        to={{
          pathname: "/trip",
        }}
        className="btn custom-button">
          Create New Trip
      </Link>
    )
  }
}

It might be better to have the state even higher up in the App component, but you didn't provide that, so this has to do :)

Upvotes: 2

Related Questions