useSelector not updating when store has changed in Reducer. ReactJS Redux
JavascriptReactjsEcmascript 6React ReduxReact HooksJavascript Problem Overview
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
}
};
}
Javascript Solutions
Solution 1 - Javascript
> 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 - Javascript
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 - Javascript
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 })
}
Solution 4 - Javascript
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 5 - Javascript
There is an interesting edge case that can happen when modifying the file where you create your Store.
If the file where you have your redux store Provider
component (usually App.tsx) does not get reloaded by React's hot module reloader (HMR) but the redux store file gets modified and therefore reloaded by HMR, a new store is created and the store Provider
in your App.tsx can actually end up passing an old instance of your redux store to useSelector
.
I have left the following comment in my setup-store.ts
file:
/**
* Note! When making changes to this file in development, perform a hard refresh. This is because
* when changes to this file are made, the react hot module reloading will re-run this file, as
* expected. But, this causes the store object to be re-initialized. HMR only reloads files that
* have changed, so the Store Provider in App.tsx *will not* be reloaded. That means useSelector
* values will be querying THE WRONG STORE.
*/