componentWillReceiveProps this.props and nextProps always the same

Viewed 3946

I have a parent component ProductTest that holds state as the source of truth. The state is an array of variant items. Each item is simplified for this example and has a title.

When a Variant's title is updated, the child will call it's handler callback via the prop passed by the parent. The parent then updates state, and the child will then have a new title property.

What I am seeing is that the child's componentWillReceiveProps function will display the same value for this.props and nextProps. I would think that since the value is supplied by the parent, the two would be different.

I'm not sure what I'm doing incorrectly.

Here is the parent ProductTest component:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import axios from 'axios'
import Variant from './variants/Variant'

import { REACT_APP_API_URL, REACT_APP_SITE_KEY } from '../../../shared/vars'

import '../../../css/variants.css'

class ProductTest extends Component {
    constructor(props) {
        super(props)
        this.state = {
            variants: []
        }

        this.handleVariantTitleChange = this.handleVariantTitleChange.bind(this)
        this.handleVariantValueChange = this.handleVariantValueChange.bind(this)
    }

    componentDidMount() {
        this.loadData()
    }

    loadData() {
        const apiEndpoint = 'products/some-product'
        const AUTH_HEADER = { Authorization: REACT_APP_SITE_KEY }
        return axios.get(REACT_APP_API_URL + apiEndpoint, { headers: AUTH_HEADER })
            .then((response) => {
                this.setState({ variants: response.data.product.variants })
            })
            .catch((err) => {
                console.log("YA GOOFED BUD!", err)
            })
    }

    handleVariantTitleChange(e, i) {
        let { variants } = this.state
        const { value } = e.target

        variants = variants.map((item, index) => {
            item.title = i === index ? value : item.title
            return item
        })

        this.setState({ variants })
    }

    handleVariantValueChange(e, id) {
        let { variants } = this.state
        const { value } = e.target

        variants = variants.map(variant => {
            variant.items = variant.items.map(item => {
                item.title = id === item.id ? value : item.title
                return item
            })
            return variant
        })

        this.setState({ variants })
    }

    render() {
        return (
            <div>
                {this.state.variants.map((variant, i) => {
                    return <Variant key={i}
                        variant={variant}
                        index={i}
                        changeVariantTitle={this.handleVariantTitleChange}
                        changeVariantValue={this.handleVariantValueChange}
                    />
                })}
            </div>
        )
    }
}

export default ProductTest

Here is the child Variant component:

import React, { Component } from 'react'

class Variant extends Component {
    componentWillReceiveProps(nextProps){
        console.log(this.props, nextProps)
    }

    render() {
        const { variant, index } = this.props

        return (
            <div className="variant-wrapper" data-new="new_value">
                <div className="ajax-error" data-hbs-id="{{id}}"></div>
                <div className="varient-item-titles">
                    <div className="variant-item-title">
                        Title
                    </div>
                    <div className="varient-attribute-titles">
                        <div className="variant-attribute">Value</div>
                    </div>
                </div>
                <div className="variant-item-content">
                    <div className="variant-label">
                        <input type="text" name="variant_label" value={variant.title} placeholder="Size, colour, etc."
                            onChange={e => this.props.changeVariantTitle(e, index)}
                        />
                    </div>
                </div>
            </div>
        )
    }
}

export default Variant

Before

enter image description here

After

enter image description here

Console.log()

These two objects are this.props and nextProps. Notice the title property is not different, as I'd expect them to be.

enter image description here

SOLUTION!

The reason for all of those is that I was not correctly altering the parent's state immutably. I think I was just changing the value at the same memory address, and hence by the time it got to the componentWillReceiveProps it was already the new value. I think...

I found that the following will work:

handleVariantTitleChange(e, i){
    const { value } = e.target

    // Immutably clone specific variant item
    let variant = Object.assign({}, this.state.data.variants[i])

    // Set new title to item
    variant.title = value

    // Set the variant to the cloned item
    let variants = this.state.data.variants
    variants[i] = variant

    this.setState({
        data: {
            ...this.state.data,
            variants
        }
    })
}
0 Answers
Related