React: update component only if new values in state are different from old

I’m building my first React app and have a feeling I’m doing something wrong :slight_smile:

Here’s the structure I’m working with:

< App (source of thruth) />
  < Settings component />
  < Timer component />

(i.e. App renders and passes props to children - Settings and Timer)

Timer needs to be updated every 1s, so I end up also updating Settings, because it’s in the same render() call. This is not necessary so in the Settings shouldComponentUpdate() hook I prevent Settings from updating unless any of the new props carry values that are different from the current ones, by comparing them one by one:

shouldComponentUpdate(nextProps, nextState){
    
    return this.state.fnStatus.name != nextProps.fnStatus.name
           || this.state.showOptions != nextState.showOptions
           || this.state.currentOption != nextState.currentOption;
}

Is there any other way to only update Timer in this setup?

Here is the codepen: https://codepen.io/pwkrz/pen/GQarYM

so I end up also updating Settings, because it’s in the same render() call

I’m not exactly sure when you mean by this but this is likely the cause of your problem. You should have the Timer component be totally independent from the Settings component. Timer can manage it’s own state and shouldn’t care about what settings is doing. Likewise, settings manages it’s own state and props and doesn’t care about what timer is doing.

As an example heres a quick ticker component:

import React, { Component } from 'react';

export default class Ticker extends Component {
  constructor(props){
    super(props);

    this.state = {
      tickVal: 0,
    }
  }

  componentWillMount(){
    this.tickerInterval = setInterval(() => {
      this.setState({
        tickVal: this.state.tickVal + 1
      })
    }, 1000)
  }

  componentWillUnmount(){
    clearInterval(this.tickerInterval);
  }

  render(){
    var {tickVal} = this.state;
    console.log('ticker rendering');
    return  <h1>{tickVal}</h1>
  }
}

and here is a quick settings component

import React, { Component } from 'react';

export default class Settings extends Component {
  render(){
    console.log('settings rendering');
    return <div>IM THE SETTINGS COMPONENT</div>;
  }
}

When you run this app in the console you will see the following logged out:

ticker rendering
settings rendering
ticker rendering
ticker rendering
ticker rendering
ticker rendering

So the ticker is rendering every second because it is displaying the tick value in an h1 tag and the browser needs to paint that. However, nothing is changing within settings so it renders once

Hope this helps a bit

1 Like

My problem was that I wanted to sometimes adjust the Setting component based on the Timer state (break/session switch in a pomodoro clock).

But reading your suggestion, I guess I could send info about the switch from the timer to the main component and then down to Settings. Thanks!

Sure - you could do something like let <App/> know when the state of the clock has switched to from “running” to “paused”, and then app will pass the “paused” prop down into <Settings/> and then settings will update.

In this case you’re basically using <App> as a state manager for its children which I think is pretty good for a small app like the pomodoro. For a large app you would probably just want to use Redux :slight_smile: