I'm trying to get to grips with controlled forms using React & Redux, and I've got it working so that when I'm typing in the input field the state is updating and passing down to the input component as intended.
However, in my reducer, when I console log the previous state, the form field's value doesn't contain the value from before the new character was typed, it already has the new character.
My reducer:
import initialState from '../state/form'
const form = (prevState = initialState, action) => {
switch (action.type) {
case 'INPUT': {
console.log(prevState) // the value here equals "test"
debugger // the value here equals "tes"
let newFields = prevState.fields
newFields[action.field].value = action.value
return Object.assign({}, prevState, {
fields: newFields
})
}
default: {
return prevState
}
}
}
export default form
If my input field contains the text "tes", I can then add a "t" and the action is dispatched as intended, but when it gets to this reducer, I console log the previous state and the field's value is "test", not "tes".
I'm expecting the previous state to have "tes", and the reducer to return the new state with "test".
In my container I have:
const dispatchToProps = (dispatch, ownProps) => {
return {
control: (e) => {
dispatch({
type: 'INPUT',
form: ownProps.formId,
field: e.target.getAttribute('name'),
value: e.target.value
})
},
clear: () => {
dispatch({
type: 'CLEAR_FORM',
form: ownProps.formId
})
}
}
}
So my input component is being passed the 'control' function. I've since used a debugger statement right next to the console.log in the reducer code above, and using Chrome's dev tools, this show prevState to have exactly what I expected (tes, not test). The console.log is still logging "test" though!
So it appears my redux implementation may be ok, there's just some voodoo somewhere as console.log(prevState) == "test" and the debugger allows me to watch the prevState variable and shows that it equals "tes", as expected!
Thanks for your answer @Pineda. When looking into bizarre console log behaviour (as you were typing your answer) I came across the variables are references to objects fact (here) - I've stopped mutating my state, and updated my reducer:
import initialState from '../state/form'
const form = (state = initialState, action) => {
switch (action.type) {
case 'INPUT': {
return Object.assign({}, state, {
fields: {
...state.fields,
[action.field]: {
...state.fields[action.field],
value: action.value
}
}
})
}
default: {
return state
}
}
}
and now it's all working correctly. I may have been appearing to get away with mutating state due to errors in my mapStateToProps
method, which had to be resolved for the new reducer to work correctly.