etayluz
etayluz

Reputation: 16416

React: component's render() is NOT invoked after this.setState() is called

My component's render() method is NOT called after a change of state with this.setState().

This is the method where the component's state is changed (it is invoked and I have verified this):

  handlePhoneNumberChange = name => (event) => {
    const { contactPhone } = this.state;

    const index = name;
    const phoneEntry = contactPhone[index];
    phoneEntry.phoneNumber = event.target.value;
    this.setState({ contactPhone });
  };

This is the component's initial state:

  constructor(props) {
    super(props);

    this.state = {
      contactPhone: [{ phoneNumber: '', phoneType: '' }],
    };
  }

This is how the TextField is rendered:

{contactPhone.map((phone, index) => (
    <div key={index}>
                <TextField
                  id={String(Math.random())}
                  label="Phone number"
                  type="tel"
                  value={phone.phoneNumber}
                  onChange={this.handlePhoneNumberChange(index)}
                  placeholder="Contact phone number"
                  margin="normal"
                />
                <br />
    </div>
))}

Why isn't render being invoked after setState()?

Upvotes: 0

Views: 6364

Answers (5)

bamse
bamse

Reputation: 4373

Your handlePhoneNumberChange function mutates state directly. The React docs states: Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.

The simplest fix is to clone contactPhone, change it and pass it to state

handlePhoneNumberChange = index => (event) => {
  const contactPhone = [...this.state.contactPhone]; // at this point contactPhone !== this.state.contactPhone
  const phoneEntry = contactPhone[index];
  phoneEntry.phoneNumber = event.target.value;
  this.setState({ contactPhone });
};

Should work!


const cloneArray = [...someArray]; // spread syntax

is the same as

const cloneArray = someArray.slice(0);

Both produce an array that contains the same values as someArray but are not the same array.

Upvotes: 4

dvvtms
dvvtms

Reputation: 627

I think you need revisit your handlePhoneNumberChange function: You here not making changes, you setting same value twice. Reference for Array contactPhone is same

handlePhoneNumberChange = name => (event) => {
    // its destructed, but not changed later
    const { contactPhone } = this.state;
    //  no changes
    const index = name;
    const phoneEntry = contactPhone[index];
    phoneEntry.phoneNumber = event.target.value;

    // you set same value, nothing changed
    this.setState({ contactPhone });
  };

Upvotes: 0

etayluz
etayluz

Reputation: 16416

I don't know why it works but it solves it:

I changed:

this.setState({ contactPhone });

To:

this.setState({ contactPhone: [...contactPhone] });

[...contactPhone] simply creates a new object. But now the render() is being called. Very weird.

Upvotes: 0

Dhananjai Pai
Dhananjai Pai

Reputation: 6015

You are not modifying the state here. You are setting it to the same value you had initially. Update the code as below.

handlePhoneNumberChange = name => (event) => {
    const { contactPhone } = this.state;
    const index = name;
    const phoneEntry = contactPhone[index];
    phoneEntry.phoneNumber = event.target.value;
    contactPhone = [...contactPhone,phoneEntry]
    this.setState({ contactPhone });
};

Upvotes: 0

Cesar N Mejia Leiva
Cesar N Mejia Leiva

Reputation: 1721

It is a terrible idea to assign the list key to a random stringified number, key={String(Math.random())}, because the key will change with each render call and React will not be able to reconcile the virtual DOM properly when this.setState is invoked. React keeps track of which list items have changed by diff-ing list items with the same key.

See link below for further details on React reconcilation: https://reactjs.org/docs/reconciliation.html#keys

Upvotes: 0

Related Questions