1

I have a component that must make an HTTP request based off new props. Currently it's taking a while to actually update, so we've implemented a local store that we'd like to use to show data from past requests and then show the HTTP results once they actually arrive.

I'm running into issues with this strategy:

componentWillRecieveProps(nextProps){
  this.setState({data:this.getDataFromLocalStore(nextProps.dataToGet)});
  this.setState({data:this.makeHttpRequest(nextProps.dataToGet)});
  //triggers single render, only after request gets back
}

What I think is happening is that react bundles all the setstates for each lifecycle method, so it's not triggering render until the request actually comes back.

My next strategy was this:

componentWillRecieveProps(nextProps){
  this.setState({data:this.getDataFromLocalStore(nextProps.dataToGet)});
  this.go=true;
}
componentDidUpdate(){
  if(this.go){
    this.setState({data:this.makeHttpRequest(this.props.dataToGet)});
  }
  this.go=false;
}
//triggers two renders, but only draws 2nd, after request gets back

This one SHOULD work, it's actually calling render with the localstore data immediately, and then calling it again when the request gets back with the request data, but the first render isnt actually drawing anything to the screen!

It looks like react waits to draw the real dom until after componentDidUpdate completes, which tbh, seems completely against the point to me.

Is there a much better strategy that I could be using to achieve this?

Thanks!

2 Answers 2

1

One strategy could be to load the data using fetch, and calling setState when the data has been loaded with the use of promises.

componentWillRecieveProps(nextProps){
  this.loadData(nextProps)
}

loadData(nextProps){
    // Create a request based on nextProps
    fetch(request)
        .then(response => response.json())   
        .then(json => this.setState({updatedValue: json.value})
}
Sign up to request clarification or add additional context in comments.

2 Comments

Right, but assuming my "request" function is the same as your "loadData" (any sort of async request, fetch, ajax, whatever it may be), how would you render the component with some default data ("dataFromStore()") before the async request gets back?
@JoeRoddy - You should have a getInitialState function that returns the default value for that data. The initial data will then be empty, or some default value, until your async request completes and updates the data.
0

I use the pattern bellow all the time (assuming your request function supports promises)

const defaultData = { /* whatever */ }

let YourComponent = React.createClass({
  componentWillRecieveProps: function(nextProps) {
    const that = this

    const cachedData = this.getDataFromLocalStore(nextProps)
    that.setState({
      theData: { loading: true, data: cachedData }
    })

    request(nextProps)
      .then(function(res) {
        that.setState({
          theData: { loaded: true, data: res }
        })
      })
      .catch(function() {
        that.setState({
          theData: { laodingFailed: true }
        })
      })
  },

  getInitialState: function() {
    return {
      theData: { loading: true, data: defaultData }
    };
  },

  render: function() {
    const theData = this.state.theData

    if(theData.loading) { return (<div>loading</div>) } // you can display the cached data here
    if(theData.loadingFailed) { return (<div>error</div>) }

    if(!theData.loaded) { throw new Error("Oups") }

    return <div>{ theData.data }</div>
  }

)}

More information about the lifecycle of components here

By the way, you may think of using a centralized redux state instead of the component state.

Also my guess is that your example is not working because of this line:

this.setState({data:this.makeHttpRequest(this.props.dataToGet)});

It is very likely that makeHttpRequest is asynchronous and returns undefined. In other words you are setting your data to undefined and never get the result of the request...

Edit: about firebase

It looks like you are using firebase. If you use it using the on functions, your makeHttpRequest must look like:

function(makeHttpRequest) {
  return new Promise(function(resolve, reject) {
    firebaseRef.on('value', function(data) {
      resolve(data)
    })
  })
}

This other question might also help

8 Comments

The problem is that my goal isn't to load "default data" persay, it's some data that I generate on then client that is based on nextProps. This strategy would let me have a default state of "loading" or something, but my goal is to grab results of the request that I'm making with nextProps based out of my local history of request results, and show that on the screen until the request comes back.
You mean that your request function takes in input what you already have in the local store ?
Yes so, based on something in nextProps, I'm making an http request. If I have a cached version of that request, I want to display the previous result until the response gets back.
That doesn't work (maybe I'm just an idiot). Essentially that is the first example I gave/how I would think it works intuitively, but it seems like react batches the setStates in componentwillreceiveprops and executes them all at the same time (and only after the request gets back).
I added a line in my answer about asynchronous calls. Especially, http requests are asynchronous in javascript, meaning that you likely cannot call this.makeHttpRequest the way you do. Can you tell use what is inside it ?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.