4

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.

Community
  • 1
  • 1
Sven
  • 12,997
  • 27
  • 90
  • 148
  • Are you using Redux? If so, you can dispatch an action from `` – Omri Aharon Jan 05 '17 at 20:49
  • @OmriAharon Unfortunately not, plain React for now. – Sven Jan 05 '17 at 20:50
  • Then since the only way components receive information (in the "React way") is state/props, you pretty much covered both options. I'd use your first option that you got to work. By the way - `` seems like a perfectly fine place to call this API (or at least trigger it), and `` should be perfectly happy with just receiving and displaying data. – Omri Aharon Jan 05 '17 at 20:55
  • A third option might be to fetch in the `` component, then call a function in `` with the data you received, which would in turn just pass it to ``, so basically `` handles the call instead of ``. – Omri Aharon Jan 05 '17 at 20:57

2 Answers2

2

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.

Rafael Quintanilha
  • 1,413
  • 11
  • 10
0

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.

Nitish Phanse
  • 552
  • 1
  • 9
  • 16