Hai Na Zheng
Hai Na Zheng

Reputation: 177

React: setState conditionally in componentDidUpdate not working

I'm trying to do a simple greeting text in react, but it's not working! There's no error complaining in the console, in fact it simply ignored everything in the componentDidUpdate and only showed the original dayPeriod text 'morning' in the **this,state**. The code is as below:

class MainContent extends Component {
    constructor(props) {
        super(props)
            dayPeriod: 'morning'
        }
    }
}

componentDidUpdate(preProps, preState) {
        //handleDayPeriod
        let hour = new Date().getHours()
        let newDayPeriod = hour < 12 ? 'Morning' : hour >= 12 && hour < 17? 'Afternoon' : 'Evening'
        if (preState.dayPeriod && preState.dayPeriod !== newDayPeriod) {
            this.setState({
                dayPeriod: newDayPeriod
            })
        }
    }

render() {
    return (
            <div className='greeting'>Good {this.state.dayPeriod}!</div>
    )
}

Can anyone help see what's the problem?

Upvotes: 0

Views: 172

Answers (2)

Tarukami
Tarukami

Reputation: 1160

componentDidUpdate does not fire on the very first render. So if you want to do the things like that, it is better to use componentDidMount to run the function checking from time to time if day period is changed or not.

class MainContent extends Component {
  constructor(props) {
    super(props)
    this.state = {
      itemsReversed: ['Event 1', 'Event 2', 'Event 3', 'Event 4', 'Event 5'].reverse(),
      allChecked: false,
      isChecked: true,
      dayPeriod: 'morning',
      intervalId: null
    }
    this.btnSelectAll = React.createRef()
  }
  // when component is created we start the function checking if the greeting needs update
  // the interval id also is saved into state for cleaning later
  // now it works every 1sec but for practical purpose it s better to set up as 60000 (1min)
  componentDidMount(){
    this.setState({intervalId: setInterval(this.changeDayPeriod, 1000)})
  }
  // this section clear up the timer to avoid memory leak:
  componentWillUnmount() {
    clearInterval(this.state.intervalId)
  }
  // this function is yours but now it compares current time value with the stored one, not prev and next ones:
  changeDayPeriod = () => {
    const hour = new Date().getHours()
    const newDayPeriod = hour < 12 ? 'Morning' : hour >= 12 && hour < 17? 'Afternoon' : 'Evening'
    if (this.state.dayPeriod !== newDayPeriod) {
      this.setState({
          dayPeriod: newDayPeriod
      })
    }
  }

  render() {
    return (
      <div className='greeting'>Good {this.state.dayPeriod}!</div>
    )
  }
}

Upvotes: 0

Nicholas Tower
Nicholas Tower

Reputation: 84947

componentDidUpdate is the wrong place to be putting this code. componentDidUpdate gets called when new props get passed into the component, or when the component's state changes. If you just want to set the initial value of the state, put the code into your constructor:

constructor(props) {
  super(props)
  let hour = new Date().getHours()
  let newDayPeriod = hour < 12 ? 'Morning' : hour >= 12 && hour < 17 ? 'Afternoon' : 'Evening'
  this.state = {
    dayPeriod: newDayPeriod
  }
}

Note that as time passes, this is not going to update. If you need it to do so, you'll need to add some extra code to update the state.

In the following code, componentDidMount means this code will get run immediately after the component is mounted (ie, after it's created for the first time). componentWillUnmount happens right before the component is about to be destroyed.

class MainContent extends Component {
  constructor () {
    // same as above
  }

  componentDidMount() {
    this.intervalId = setInterval(() => {
      let hour = new Date().getHours()
      let newDayPeriod = hour < 12 ? 'Morning' : hour >= 12 && hour < 17 ? 'Afternoon' : 'Evening'
      if (newDayPeriod !== this.state.dayPeriod) {
        this.setState({ dayPeriod: newDayPeriod })
      }
    }, 60000); // Every 10 minutes. I chose this pretty arbitrarily
  }

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

The above code just blindly tries to update every 10 minutes. That's the simplest approach, and should be fine for many cases. But if you wanted to be more precise, then instead of having a repeating interval, you could calculate a timeout that you know will go off right when we roll to a new day period.

Upvotes: 1

Related Questions