Search code examples
reactjsreduxreact-reduxnormalizr

Can I force a rerender with setState even if the data hasn't changed? My GUI won't update?


So the problem I'm having right now is on first click, my GUI re-renders. But on second click, it does not re-render. I believe it's because I am not updating the state of "graphicLayers" which my render is binding through the "graphicLayers.map". That's my theory anyway (even though it works on first click? but not the 2nd click or anything after).

I tried forcing an setState update of graphicLayers, but it doesn't seem to be working. Like this:

let graphicLayersCopy = Object.assign([], this.state.graphicLayers);
this.setState({graphicLayers: graphicLayersCopy});

but that's not working. I know through the debugger that it's setting the data correctly, and if I refresh (it saves state and reloads the state), the GUI is then rendered correctly.

Is there anyway I can force a re-render of a variable some how even if it doesn't change value?

constructor

constructor(props, context) {
    super(props, context);

    this.state = {
      graphicLayers: [id1, id2, id3],
      graphicLayersById: {
        id1: { ... },
        id2: { ... },
        id3: { ... }
      }

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

render

render() {
    return (        
    <div>
      {this.state.graphicLayers.map((id) =>
        <GraphicLayer addLayerClick={this.addLayerClick.bind(this)} />
      )}
    </div>
    );
   }

addLayerClick

addLayerClick() {
  ... change some property in graphicLayersById dictionary ...
  self.setState({ graphicLayersById: newDataHere });
}

EDIT: I found the problem on my end, and it's not exactly shown here.

So my addLayerClick() actually calls another functions that is listening to a call and it sets the state inside. it's weird because the setState gets called in the callback function, but i got it to work by putting the setState in the addLayerClick() itself.. still dont know why this doens't work but i will upvote all of you at least

listenFunction() {

let self = this;

this.firebaseWrapper.storage.on('graphicLayersById', function (save) {
  if (save) {
    self.setState({ graphicLayersById: save });  // FOR SOME REASON THIS DOESN'T UPDATE THE GUI THE 2nd CLICK.  The data is correct though and I see it going here on a breakpoint, but GUI won't update unless I setState in the actual button
  }
  else {
    self.setState({ graphicLayersById: undefined });
  }
});

}


Solution

  • In addLayerClick() you're only updating graphicLayersById, but rendering depends on graphicLayers. You should be updating the graphicLayers state in addLayerClick() as well.

    addLayerClick() {
      this.setState({
        graphicLayers: ...
        graphicLayersById: ....
      });
    }
    

    On a side note, you shouldn't bind methods inside render() since that creates a brand new function on every render (and could impact performance). Instead of

    <GraphicLayer addLayerClick={this.addLayerClick.bind(this)} />
    

    do

    <GraphicLayer addLayerClick={this.addLayerClick} />
    

    and leave the binding in your constructor (the way you already have).