Steve Waters
Steve Waters

Reputation: 3548

React: Boolean variables to indicate a successful fetch before rendering

I want to use a Boolean variable to make rendering only possible after two different fetches are BOTH done. I'm quite new to both React AND JavaScript, so bear with me...

Problem:

Relevant part of my code:

class AddressBook extends Component {

  constructor() {
    super();
    this.state = {
      personData: [],
      projectData: [],
      hasSearched: false,
      simplePersonUrl: 'path-to-api/persons',
      simpleProjectUrl: 'path-to-api/projects',
    }
  }

  addressBookSearch(value) {
    var hasSearchedPersons = false;
    var hasSearchedProjects = false;

    if (value !== '' && value.length > 2) {
      const urlsToUse = this.apiUrlsToUse();

      fetch(`${urlsToUse["personUrl"]}${value}`)
        .then((response) => response.json()).then((responseJson) => {
        this.setState({personData: responseJson}).then(() => this.hasSearchedPersons = true)
      })

      fetch(`${urlsToUse["projectUrl"]}${value}`)
        .then((response) => response.json()).then((responseJson) => {
        this.setState({projectData: responseJson}).then(() => this.hasSearchedProjects = true)
      })
    }

    if (hasSearchedPersons == true && hasSearchedProjects == true) {
      this.setState({
        hasSearched: true
    });
    }
  }

}

Then I have this conditional rendering in the render method:

{(this.state.hasSearched && (this.state.personData.length > 0 || this.state.projectData.length > 0)) &&
      <div>
        <Paper style={{boxShadow: 'none'}}>
          <SearchResultTab personData={this.state.personData} projectData={this.state.projectData}/>
        </Paper>
      </div>
}

{(this.state.hasSearched && this.state.personData.length <= 0 && this.state.projectData.length <= 0)
      ? <div style={{textAlign: 'center'}}>No results..</div>
      : null
}

The rendering works great otherwise, but the problem is that the rendering happens before the second fetch is not done when the rendering already happens. So I'm now trying to prevent the rendering with some Boolean values. That is the part that doesn't work.

Now, I know that the last part in the promises is wrong, as it gives me:

Uncaught (in promise) TypeError: Cannot read property 'then' of undefined

it's just there to indicate what I want to do:

set that boolean hasSearchedPersons and hasSearachedProjects as true WHEN the fetch is successfully done.

Then as those both are done, the boolean hasSearched, in the state would be set to true and the rendering would happen with both fetches done.

How to do this? My head is about to explode. Thank you.

Upvotes: 2

Views: 4970

Answers (1)

The Reason
The Reason

Reputation: 7973

Just couple notes about setState. From react docs:

setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state.

It means that all the time you change your state, using setState you re-render your component. Before you set hasSearch to true you re-rendered your component twice. So to avoid unnecessary re-redndering you should use it once after your fetches are done. It's possible with Promise.all() (already mentioned in a comments).

Promise.all([
  fetch(`${urlsToUse["personUrl"]}${value}`),
  fetch(`${urlsToUse["projectUrl"]}${value}`)
]).then(responses => Promise.all(responses.map(res => res.json())
  .then(values => {
    // doing something with it.
    // once you are done use setState
    this.setState({ hasSearched: true })
  })

Hope it will help.

Upvotes: 3

Related Questions