Sven
Sven

Reputation: 13275

React: Trigger method in child component from different child component

I've created two components in React, <Search /> and <List /> that both are children of <App />.

When a button in <Search /> is clicked, I want to fetch something from an API and display the results in <List />. While I got this working by doing the fetch inside <App /> and passing the response as prop to <List />, I would prefer to encapsulate the fetch inside <List />.

Unfortunately I am having a hard time finding a way to do this. 'The React way' probably would be to do this via some clever prop passing, but I haven't found a neat way to do this – even a 'shouldFetch' boolean would need to be reset after the fetch which seems cumbersome and would trigger unnecessary renders.

This answer uses refs for something similar which might work, but actually I am a bit hesitant to try it since refs seem to be a bit dirty, according to the React docs, as they "imperatively modify a child outside of the typical dataflow".

How can I instruct my <List /> component to do something after a button in <Search /> has been clicked?

If required I can supply code – but hoping this question is simpler than it seems to me.

Upvotes: 5

Views: 4128

Answers (2)

Nitish Phanse
Nitish Phanse

Reputation: 562

This will be a good time to use a store, which will keep the entire state of your app as a single JS object. Flux, Redux are 2 excellent architectures which complement React. Basically, your components listen for changes in the store, when ever any action ( making an api call / click etc ) is carried out the store gets updated and the ui changes. A large scale react application can get very messy to handle with lots of children.

Also completely agree with the above answer, let the <List /> component be stateless, and only render based on props. Hence having the functions written in the parent <App/> and have the data passed down is a good idea.

Upvotes: 0

Rafael Quintanilha
Rafael Quintanilha

Reputation: 1432

A natural option would be to create a state boolean variable in <App />, triggered when a button is pressed in <Search /> and then use some logic in <List /> to fetch data when the boolean state went from false to true.

For example:

class App extends React.Component {
  constructor() {
    super();
    this.state = { fetchData: false }
  }

  render() {
    return (
      <div>
        <Search onClick={() => this.setState({fetchData: true})} />
        <List shouldFetch={this.state.fetchData} onFetch={() => this.setState({fetchData: false})} />
      </div>
    )
  } 
}

Then in your <List /> component:

class List extends React.Component {
  componentWillReceiveProps(nextProps) {
    if ( !this.props.shouldFetch && nextProps.shouldFetch ) {
      // Fetch data here and set it in state, for example
      // After fetch, don't forget to call onFetch() to reset the boolean
    }
  }

  ... // more code
}

Although this will work, it is not bad to use <App /> as the source of data. This way you can abstract pure components that only handles UI but have no network logic. This is often a good model and helps you to reuse components in the future.

Upvotes: 3

Related Questions