简体   繁体   中英

Child component doesn't rerender but parent component does rerender. How to make child component rerender?

Parent component does rerender upon receiving new props but its child component doesn't rerender. Child components only render for the first time and never rerender nor receive props from the redux store

I'm getting updated data from redux store in Parent component but not in the child components. Child components only receive data from redux store when they render for the first time

My Parent Component Home.js

Object seaFCLJSON look like this const seaFCLJSON ={"rates": {"sort":"faster", "someOther": "someOtherValues"}};

when the redux store gets updated, seaFCLJSON looks like this const seaFCLJSON ={"rates": {"sort":"cheaper","someOther": "someOtherValues"}};

class Home extends Component {
state = {
    seaFCLJSON: {}
  };

componentDidMount = () => {
     this.setState({ seaFCLJSON: this.props.seaFCLJSON });
  };

 componentWillReceiveProps = nextProps => {
    if (this.state.seaFCLJSON !== nextProps.seaFCLJSON) {
      this.setState({ seaFCLJSON: nextProps.seaFCLJSON });
    }
  };

 render() {
    const { seaFCLJSON } = this.props;
 return (
       <>
    {!isEmpty(seaFCLJSON) && seaFCLJSON.rates && seaFCLJSON.rates.fcl ? (
          <FCLContainer fclJSON={seaFCLJSON} />
        ) : null} //it never rerenders upon getting updated data from redux store

    <h5>{JSON.stringify(seaFCLJSON.rates && seaFCLJSON.rates.sort)}</h5> //it rerenders everytime upon getting updated data from redux store
       </>
     );
  }
}

const mapStateToProps = state => {
  return {
    seaFCLJSON: state.route.seaFCLJSON
  };
};

export default connect(
  mapStateToProps,
  actions
)(Home);

isEmpty.js

export const isEmpty = obj => {
  return Object.entries(obj).length === 0 && obj.constructor === Object;
};

My Child Component FCLContainer.js

class FCLContainer extends Component {
  state = {
    seaFCLJSON: {}
  };
  componentDidMount = () => {
    this.setState({ seaFCLJSON: this.props.seaFCLJSON });
  };

  componentWillReceiveProps = nextProps => {
    console.log("outside state value: ", this.state.seaFCLJSON);
    if (this.state.seaFCLJSON !== nextProps.seaFCLJSON) {
      this.setState({ seaFCLJSON: nextProps.seaFCLJSON });
      console.log("inside state value: ", this.state.seaFCLJSON);
    }
  };


  render() {
    const { seaFCLJSON } = this.state;
    console.log("rendering .. parent props: ", this.props.fclJSON);
    console.log("rendering .. redux store props: ", this.props.seaFCLJSON);

    return (
      <>
        <div className="home-result-container">
          <div>
          <h5>This child component never rerenders :(</h5>
          </div>
        </div>
      </>
    );
  }
}

const mapStateToProps = state => {
  return {
    seaFCLJSON: state.route.seaFCLJSON
  };
};

export default connect(
  mapStateToProps,
  actions
)(FCLContainer);

I don't know whether there are problems in Parent component or problems in the child component. componentWillReceiveProps gets invoked in the parent component but not in the child component. Please ignore any missing semi-colon or braces because I have omitted some unnecessary codes. Edit 1: I just duplicated value from props to state just for debugging purposes.

I will appreciate your help. Thank you.

Edit 2 : I was directly changing an object in redux actions. That's the reason CWRP was not getting fired. It was the problem. For more check out my answer below.

componentWillReceiveProps will be deprecated in react 17, use componentDidUpdate instead, which is invoked immediately after updating occurs Try something like this:

componentDidUpdate(prevProps, prevState) {
    if (this.prevProps.seaFCLJSON !== this.props.seaFCLJSON) {
      this.setState({ seaFCLJSON: this.props.seaFCLJSON });
    }
  };

At the first place it is absolutely meaningless to duplicate value from props to state, what is the meaning of it? Totally pointless, just keep it in props

About your issue - most probably this condition doesnt match, thats why child component doesnt trigger

!isEmpty(seaFCLJSON) && seaFCLJSON.rates && seaFCLJSON.rates.fcl

check it in debugger

As far as I can see, your problem is that you pass the following to your child component:

<FCLContainer fclJSON={seaFCLJSON} />

But you assume that you receive a prop called 'seaFCLJSON':

 componentDidMount = () => {
this.setState({ seaFCLJSON: this.props.seaFCLJSON });

};

You should change your code to:

<FCLContainer seaFCLJSON={seaFCLJSON} />

Apart from that, as @Paul McLoughlin already mentioned, you should use the prop directly instead of adding it to your state.

I found the issue I was directly mutating the object in actions. I only knew state should not be directly mutated in class or inside reducer . I changed the actions where I was directly changing an object and then saving it in redux store via dispatch and, then I received the updated props in CWRP. This really took me a lot of times to figure out. This kind of issue is hard to find out at least for me. I guess I get this from https://github.com/uberVU/react-guide/issues/17

A lesson I learned: Never directly mutate an Object

I changed this

//FCL sort by faster
export const sortByFasterFCLJSON = () => async (dispatch, getState) => {
  let seaFCLJSON = getState().route.seaFCLJSON;
  if (!seaFCLJSON.rates) return;
  seaFCLJSON.rates.fcl = _.orderBy(
    seaFCLJSON.rates.fcl,
    ["transit_time"],
    ["asc"]
  );

  seaFCLJSON.rates.sort = "Faster"; //this is the main culprit

  dispatch({ type: SET_SEA_FCL_JSON, payload: seaFCLJSON });
};

to this

//FCL sort by faster
export const sortByFasterFCLJSON = () => async (dispatch, getState) => {
  let seaFCLJSON = getState().route.seaFCLJSON;
  if (!seaFCLJSON.rates) return;
  seaFCLJSON.rates.fcl = _.orderBy(
    seaFCLJSON.rates.fcl,
    ["transit_time"],
    ["asc"]
  );

  // seaFCLJSON.rates.sort = "Faster"; //this was the main culprit, got lost

  seaFCLJSON = {
    ...seaFCLJSON,
    rates: { ...seaFCLJSON.rates, sort: "Faster" }
  };

  dispatch({ type: SET_SEA_FCL_JSON, payload: seaFCLJSON });
};

the power of not mutating data

Side note: Redux Troubleshooting

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM