React setState for nested object while preserving class type

Viewed 227

Extending from this question React Set State For Nested Object

The way to update nested state is to decompose the object and re-construct it like the following:

this.setState({ someProperty: { ...this.state.someProperty, flag: false} });

However, this is a problem for me, as it will not preserve the component class (More detail below). I am aiming for my state to be structured like the following:

this.state = {
    addressComponent: {
        street: '',
        number: '',
        country: ''
    },
    weather: {
        /* some other state*/
    }
}

To make my life easier, I created a simple address class that builds the object for me and have some other utility function such as validation.

class AddressComponent {
    constructor(street, number, country) {
        this.street = street;
        this.number = number;
        this.country = country;
    }

    validate() {
        if (this.street && this.number, this.country) {
            return true;
        }
        return false;
    }
}

This allows me to do is change the initialization of state to be:

this.state = {
    addressComponent : new AddressComponent(),
    weather: new WeatherComponent();
 }

 this will allow my view component to perform nice things like

if (this.state.addressComponent.validate()) {
    // send this state to parent container
}

The problem with this approach is that if I want to mutate one single piece of information like country and use the above Stack Overflow approach such as:

this.setState({addressComponent: {...addressComponent, country: 'bluh'}})

Doing this will mean that the resolting addressComponent is no longer part of AddressComponent class hence no validate() function

To get around it I cound recreate a new AddressComponent class every time like:

this.setState({addressComponent: new AddressComponent(this.state.street, this.state.number, 'bluh');

But this seems weird.

Am I doing this wrong? Is there a better approach? Is it acceptable to use classes like this with react?

3 Answers

I think what you said about reinstantiating your class each time you update it in your state is the cleanest way to ensure your validate() method is called at that time. If it were me, I would probably write it as:

const { addressComponent } = this.state;
this.setState({ addressComponent: new AddressComponent(addressComponent.street, addressComponent.number, 'bluh') });

It's undesirable to use anything but plain objects for React state to avoid situations like this one. Using class instances will also make serialization and deserialization of the state much more complicated.

The existence of AddressComponent isn't justified, it doesn't benefit from being a class.

The same code could be rewritten as functional with plain objects:

const validateAddress = address => !!(street && address.number && address.country);
...

if (validateAddress(this.state.address)) {
    // send this state to parent container
}

You can create a reusable function to update a complex state like this.

updateState = (option, value) => {
  this.setState(prevState => ({
    addressComponent: {
      ...prevState.addressComponent,
      [option]: value
    }
  })
}
Related