useSelector not updating when store has changed in Reducer. ReactJS Redux

55,278

Solution 1

NOTE: you better start using redux-toolkit to prevent references in you code its a better and almost a must way for using redux

the problem your facing is very common when handling with objects, the props do not change because you're changing an object property but the object itself does not change from the react side.

even when you're giving it a whole new object react doesn't see the property object change because the reference stays the same.

you need to create a new reference like this:

Object.assign(state.data,data);

return {
  ...state,
  data: { 
    ...state.data,
    Endereco: action.payload.logradouro,
    Bairro: action.payload.bairro,
    UF: action.payload.uf,
    Cidade: action.payload.cidade                    
  }
}

to add more you can learn about the Immer library that solves this problem.

Solution 2

It's not necessary to

Object.assign(state.data, data);

always when changing data of arrays or objects

return(
  object: {...state.object, a: 1, b: 2},
  array: [...state.array, 1, 2, 3]
)

this 3 dots (...) ensure that you create a new object. On redux you have to always create a new object, not just update the state. This way redux won't verify that your data has changed.

When having nesting objects or arrays, is the same thing

Just have attention to:

initialState = {
 object: {
     ...object,
     anotherObject:{
        ...anotherObject,
        a: 1,
        b: 2
      }
   }
}

Solution 3

Since the edit queue for elab BA is full.

The accepted answer here is what he meant by data being there

case MYCASE:
  let newDataObject = Object.assign(state.data, {...action.payload});
  // or 
  // let newDataObject = Object.assign(state.data, {key: 'value', 'key2': 'value2' ...otherPropertiesObject);
  return {
    ...state,
    ...newDataObject
  }

Solution 4

Somehow, the Object.assgin is not recognize Update with ES6 syntax.

updatedConnectors = state.connectors

This will create a reference to the current state. In ES6, that introduce the ... to make new reference.

updatedConnectors = { ...state.connectors }
.....
return  {
    ...state,
    connectors: updatedConnectors
};

use this to extract and copy new reference. That will trigger state change too

Update Sep/27/20 I've wrote some utils function to handle this, Let try this

//Utils
export const getStateSection = ({ state, sectionName }) => {

  const updatedState = { ...state }
  const updatedSection = updatedState[sectionName]
  return updatedSection
}

export const mergeUpdatedSection = ({ state, sectionName, updatedValue }) => {
  const updatedState = { ...state }
  updatedState[sectionName] = updatedValue
  return updatedState
}

Then In any reducer, It should use like this:

//reducer
const initState = {
  scheduleDetail: null,
  timeSlots: null,
  planDetail: {
    timeSlots: [],
    modifedTimeSlots: [],
    id: 0
  }

}
.....  
const handlePickTimeSlot = (state, action) => {
  let planDetail = getStateSection({ state, sectionName: 'planDetail' })
  // do stuff update section
  return mergeUpdatedSection({ state, sectionName: 'planDetail', updatedValue: planDetail })
}
Share:
55,278
Danilo Cunha
Author by

Danilo Cunha

Updated on July 09, 2022

Comments

  • Danilo Cunha
    Danilo Cunha almost 2 years

    I am changing the state in reducer. On debug I checked that the state was really changed. But the component is not updating.

    Component:

    function Cliente(props) {
        const dispatch = useDispatch()
        const cliente = useSelector(({ erpCliente }) => erpCliente.cliente)
        const { form, handleChange, setForm } = useForm(null)
    
    ...
    
    function searchCepChangeFields() {
        //This call the action and change the store on reducer
        dispatch(Actions.searchCep(form.Cep))  
            .then(() => {   
                // This function is only taking values ​​from the old state. 
                // The useSelector hook is not updating with store
                setForm(form => _.setIn({...form}, 'Endereco', cliente.data.Endereco))
                setForm(form => _.setIn({...form}, 'Uf', cliente.data.Uf))
                setForm(form => _.setIn({...form}, 'Cidade', cliente.data.Cidade))
                setForm(form => _.setIn({...form}, 'Bairro', cliente.data.Bairro))                  
            })
    }
    

    Reducer:

     case Actions.SEARCH_CEP:
            {
                return {
                    ...state,
                    data: { 
                        ...state.data,
                        Endereco: action.payload.logradouro,
                        Bairro: action.payload.bairro,
                        UF: action.payload.uf,
                        Cidade: action.payload.cidade                    
                    }
                };
            }  
    
  • Danilo Cunha
    Danilo Cunha over 4 years
    OMG! Thank you so much! Im trying to solve this for more than 3 hours and I dont see anything about this. You just save me a lot! Thank you
  • yaswanthkoneri
    yaswanthkoneri over 4 years
    initialState = { result: [], isLoading: true, isOrderCreated: false, error: undefined }; My reducer not updating this ` case PLACEORDERFAILURE: return { ...state, error: true } ` can someone help please
  • user3176403
    user3176403 about 4 years
    what is your code? did you add ... at return state?
  • Jason G
    Jason G about 4 years
    yea, i had to create a new object first, then update it. ... wasn't enough to trigger an update.
  • user3176403
    user3176403 about 4 years
    @JasonG This is call spread out object of ES6. you can search for this key work and get the idea. This regard to memory handling of JS. This way always work for me. Which version of React are you using
  • oyalhi
    oyalhi over 3 years
    Is it me or there is not a single difference with the OPs code and this answer?
  • elad BA
    elad BA over 3 years
    @oyalhi your right I edited this answer and made a mistake I canceled my last change now there is a diff
  • Adebayo
    Adebayo almost 3 years
    In Object.assign(), where did the second argument "data" come from?
  • elad BA
    elad BA almost 3 years
    @Adebayo its the new object to assign to the old data obj
  • Hugo
    Hugo almost 3 years
    where does the data object come from?
  • mjwrazor
    mjwrazor over 2 years
    @eladBA the data object is not an incoming argument so you are pulling it from an unknown location. From normal redux the only way to get to that data is from state.data. if you are putting a new object there then it would need to be {} there
  • mjwrazor
    mjwrazor over 2 years
    Doing it this way did not provide the expected results I had to go with elab AB's answer. I tried all kinds of spread operations.
  • mjwrazor
    mjwrazor over 2 years
    This also did not provide the needed results. No matter how many different ways spread was implemented I had to go with elab AB's answer.
  • att
    att over 2 years
    @eladBA many thx man. Changing to const nextDevs = [...devs] solved the problem.