Suresh Prajapati
Suresh Prajapati

Reputation: 4487

Rerender not working after states updated

I have a parent component Rides which sends data to child component via props. Here is the Child component:

class LazyLoader extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      loaderState: this.props.loaderState
    }
  }
  componentWillReceiveProps(nextProps){
    console.log(`inside componentWillReceiveProps ${nextProps.loaderState}`);
    if(this.props != nextProps) {
      this.setState({
        loaderState: nextProps.loaderState
      }, () => {console.log(`Finished setState for ${nextProps.loaderState}`)});
    }
  }

  render(){
    console.log(`loaderState: ${this.state.loaderState}`);
    return (
      this.state.loaderState ? (
        <View style={[styles.container]}>
          <ActivityIndicator style={styles.spinner} animating={true} size={60} color='#fff'/>
        </View>) : (<View/>)
    )
  }
}

And part of my parent component (here Rides) which send the updated data from its state to child's(here LazyLoader) prop:

export default class Rides extends React.Component {
  constructor(props){
    super(props);
    this.handleRidePress = this.handleRidePress.bind(this);
    this.navigateToRideDetails = this.navigateToRideDetails.bind(this);

    this.state = {
      flash: true
    };

  }

  handleRidePress(rideId){
    this.setState({
      flash: true
    }, () => {
      console.log(`Begining handleRidePress for rideId: ${rideId}`);
      if(doSomeTimeConsumingOperation){
        // Time consuming operation goes here
      }
      this.navigateToRideDetails({rideId: rideId, pingData:pingData.slice(), rideData: rideDetails});
    });
  }

  navigateToRideDetails(rideObj){

    this.setState({
      flash: false
    }, () => {console.log(`navigateToRideDetails setState cb`); this.props.navigation.navigate('RideLog', rideObj);});
  }

  render(){
    console.log(`flash: ${this.state.flash}`);
    return(
      <Gradient.FullGradient>
      <Reusables.LazyLoader loaderState={this.state.flash}/>
      <PRides rides={this.state.rideTiles} onDeletePress={this.deleteRide} navigation={this.props.navigation} onRideDetailPress={this.handleRidePress}/>
      </Gradient.FullGradient>
    )
  }
}

When handleRidePress function is called it updates the flash state using setState() but child component doesn't get rerender. I tried using shouldComponentUpdate() in both component and returned true by default, but It doesn't work.

Upvotes: 0

Views: 90

Answers (2)

Suresh Prajapati
Suresh Prajapati

Reputation: 4487

I modified navigateToRideDetails to execute setState independently as react batches state updates that occur in event handlers and lifecycle methods. Thus, if we are updating state multiple times in a function handler, React will wait for event handling to finish before re-rendering. In my case, the flash value in state was getting reset after operation gets completed. Hence ActivityIndicator was not getting displayed.

Here is modified navigateToRideDetails function

navigateToRideDetails(rideObj){
    setTimeout(() => {
      this.setState({
        flash: false
      }, ()=>{
        this.props.navigation.navigate('RideLog', rideObj);
      });
    }, 0); 
  }

This solves the issue :)

Upvotes: 0

Mayank Shukla
Mayank Shukla

Reputation: 104529

I think error is in the way you are comparing two objects here:

if(this.props != nextProps) {
    ....
}

This is not the correct way to check whether two objects are same or not, check the values:

if(this.props.loaderState != nextProps.loaderState) {
    ....
}

Check this answer How to determine equality for two JavaScript objects?

Check this snippet:

let a = {b: true};

let b = {b: true};

console.log(a == b);    //false

Upvotes: 1

Related Questions