Wai Yan Hein
Wai Yan Hein

Reputation: 14821

Updating the field of the nested object not re-rendering the views (UI) in React Redux

I am developing a Web application using React and Redux as the front-end JavaScript framework. I am having a bit of a problem with rendering view and updating the Redux state/store value.

I have an input field which update the Redux state value. This is the event handler for onChange of my text field.

_handleNameInput= (event) => {
     this.props.updateField({
             name: 'name',
             value: event.target.value,
     })
}

In the reducer I am updating the field of the state like this

case RESTAURANT_CATEGORY_UPDATE_FIELD: {
            let freshState = { ...state };
            _.set(freshState, action.payload.name, action.payload.value);
            return freshState;
        }

I am using Lodash to update the field of the state object.

This is my default state

let defaultState = {
     name: ''
}

When the user is entering the input, I display the live state value on the view like this.

<h1>Name is: {this.props.name}</h1>

The above scenario works totally fine. It is displaying the live changes while I am changing the value of the input as well. The problem began when I use the nested object in the state. I updated the defaultState to this.

let defaultState = {
     form: {
        name: '',
     }
}

Then in the callback, I update like this.

this.props.updateField({
      name: 'form.name',
      value: event.target.value,
})

It is updating the redux state/store values of the nested property of the object. The problem is that when I display the live changes like this

<h1>{this.props.form.name}</h1>

It is not updating/rendering the view. How can I fix it?

Upvotes: 3

Views: 2632

Answers (2)

just because I was browsing this The immer thing looks cool but u can also just set a property off and on at the shallow end of your state

state.something.somethingelse = {
          ...state.something.somethingelse,
          updatedItem: posts,
          forceReRender: !state.something.somethingelse.forceReRender
        };

or

state.forceRender = !state.forceRender

Upvotes: 0

lecstor
lecstor

Reputation: 5707

When you connect your component to redux via the connect function your component behaves as a PureComponent. That is, it will perform a shallow check of the new props, comparing them to previous props, to see if the component should perform an update.

In the first instance name is a top-level prop and it's change is detected so the update is performed.

In the second instance form is the top-level prop which has not changed so the update is prevented.

As suggested in the comments, you can use immer to ensure that the form object is replaced with a new object with the updated name property.

import produce from "immer";

return produce(state, draft => {
  _.set(draft, action.payload.name, action.payload.value);
});

In the second case it would effectively do the following..

return {
  ...state,
  form: {
    ...state.form,
    name: action.payload.value
  }
};

I would suggest though, that you reconsider having an action and reducer which update arbitrarily deeply nested fields and instead use something more specific.

RESTAURANT_CATEGORY_UPDATE_FORM_FIELD

return {
  ...state,
  form: {
    ...state.form,
    [action.payload.name]: action.payload.value
  }
};

or

return produce(state, draft => {
  draft.form[action.payload.name] = action.payload.value;
});

Upvotes: 5

Related Questions