0
votes

I have a React 16.5.2 component that passes part of its state to a child component, but when the parent state changes, the child is not getting updated (the child's componentWillReceiveProps is not getting called).

Here are the basics:

class ProductForm extends React.Component {
    constructor(props){
        super(props)
        this.handlePropertiesFormChange.bind(this)
        ...
    }

    handlePropertiesFormChange(commodityProps) {
       const comm = this.state.commodity
       comm.properties = commodityProps
       this.setState({
         commodity: comm,
       })
    }

    render() {
        return(
            <ProductProperties
                commodity={ this.state.commodity }
                parentEvtHandler={ this.handlePropertiesFormChange }
            />
        )
    }

}

class ProductProperties extends React.Component {
    constructor(props) {
        super(props)
        this.state = { showDrugCatalog: false, synonyms: props.commodity.getSynonyms() || '' }
    }

    componentWillReceiveProps(nextProps) {
        // this logging does not appear after calling this.props.parentEvtHandler()
        // when ProductForm's parent CreateProduct is updated, 
        // this method is called and everything renders properly
        console.log("==== cWRP in productProperties, nextProps =  ", nextProps)
        ....
    }
}

    render() {
        // numerous calls to various methods of this.props.commodity,
        // which all work fine whenever this component is updated

    }


}

On initial render of both components, ProductProperties successfully receives ProductForm's state.commodity. When input fields are edited in ProductProperties, the component calls props.parentEvtHandler (which represents the function in ProductForm that changes state). When this call happens, ProductForm correctly updates its state -- its render() is called and references it makes to state.commodity show that the state was correctly updated.

The problem is, the new value of state.commodity is not passed to ProductProperties. In fact, it appears it is not updating ProductProperties at all, since logging on that component's componentWillReceiveProps is not triggered.

When ProductForm's parent CreateProduct is updated, the props flow correctly to ProductProperties, componentWillReceiveProps is called, and everything renders correctly.

One thing I tried: Since changes to state.commodity are only being made to a property of the object, I thought React was failing to see this object has having changed, tried updating state with a totally new clone of state.commodity created with Object.assign, but this did not fix the problem.

This problem is strange because the ProductProperties component has been in use for some time by a different parent component where this problem does not occur. I see no difference between ProductForm and the other parent component that does not exhibit this problem. (Originally ProductForm's parent CreateProduct was managing its internal state with getDerivedStateFromProps, but even when I changed this to use componentWillReceiveProps, the problem still exists).

1
The code and explanation seems quite convincing. Why don't you make a codesandbox out of your problem and let us see a complete, replicable code environment - Prasanna
Can you share pre initialised state of the ProductForm component otherwise it’s difficult to suggest you possible solution. It’s even better if you can share both the components code - Hemadri Dasari
Thanks, I've added more details -- I think the key is that ProductProperty rendering always works correctly when called -- the problem is that it's not always called when its parent's state changes. I can look at getting some working code in a sandbox if there are no further ideas. - Byofuel

1 Answers

0
votes

I found something that sort of solves the problem, although I don't think it addresses the root cause.

When I change ProductForm so it no longer passes it's pre-bound copy of handlePropertiesFormChange, but instead binds a new copy every render(), the problem goes away. Problem code:

<ProductProperties
     commodity={ this.state.commodity }
     parentEvtHandler={ this.handlePropertiesFormChange }
/>

Code that does not exhibit this problem:

<ProductProperties
     commodity={ this.state.commodity }
     parentEvtHandler={ this.handlePropertiesFormChange.bind(this) }
/>

I think that re-binding is just covering up the core problem -- with a newly bound function, React sees the need to pass new props to ProductProperties, but it should see that need based solely on state.commodity changing. This also explains why other components that use ProductProperties were not exhibiting the problem (they were binding the function anew).