0

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.

Suresh Prajapati
  • 3,991
  • 5
  • 26
  • 38

2 Answers2

1

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
Jose Paredes
  • 3,882
  • 3
  • 26
  • 50
Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
  • Ohh Thanks for pointing out that, but It doesn't solve the issue :(. I am getting log inside this condition `this.props.loaderState != nextProps.loaderState`, that means It's calling setState. – Suresh Prajapati Jul 19 '17 at 08:55
  • @SureshPrajapati sorry didn't get you, if it is calling setState then it will update the ui also, what's the issue now? – Mayank Shukla Jul 19 '17 at 08:57
  • Rerender not working after states changed updated. That means It should show `ActivityIndicator`.`loaderState` value is true still I can't see ActivityIndicator. – Suresh Prajapati Jul 19 '17 at 09:00
  • `console.log('loaderState:', this.state.loaderState);` inside render of child is printing the updated value? – Mayank Shukla Jul 19 '17 at 09:08
  • Yes, Exactly. Does changing `state` value twice is causing this problem? I set & reset the `flash` flag in one single operation – Suresh Prajapati Jul 19 '17 at 09:11
  • 1
    ooh yes, that will be the problem the transition is going very fast that is not observable in ui, use timeout first time update the state then update again after some time say 4-5 sec it will work. – Mayank Shukla Jul 19 '17 at 09:14
  • Not exactly very fast UI change, because React batches state updates that occur in event handlers and lifecycle methods. Thus, if we update state multiple times, React will wait for event handling to finish before re-rendering. – Suresh Prajapati Jul 19 '17 at 09:37
0

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 :)

Suresh Prajapati
  • 3,991
  • 5
  • 26
  • 38