The point of this document is to attempt to get people to stop shooting themselves in the foot by thinking a bit differently.
In any case, one of the "beautiful things" about the whole React+Redux community is they're totally okay with letting people shoot themselves in the foot. So, have at it, I guess, if you like that sort of thing...
There is contention on the point of treating React Action Types as things that have happened in the past.
Personally, I take these three pieces of information to inform this my point of view in this document:
From the Redux doc's Introduction page (https://redux.js.org/#the-gist)[https://redux.js.org/#the-gist]:
The whole state of your app is stored in an object tree inside a single store. The only way to change the state tree is to emit an action, an object describing what happened. To specify how the actions transform the state tree, you write pure reducers.
That's it!
From the Redux doc's Reducers page (https://redux.js.org/docs/basics/Reducers.html#reducers)[https://redux.js.org/docs/basics/Reducers.html#reducers]:
Remember that actions only describe the fact that something happened, but don't describe how the application's state changes.
Closed issue on the Redux GitHub page: (https://github.com/reactjs/redux/issues/384#issuecomment-126890410)[https://github.com/reactjs/redux/issues/384#issuecomment-126890410]
There is a lot of argument about this, but if the writer of Redux is mostly on board with the idea, that's pretty good. Even better that a lot of people are also behind it.
Treat Redux as a Dynastic Scribe! A Court Stenographer! A Drunken Friend!
You wouldn't try to control them... So don't try to control Redux!
An Action Creator:
- Exposes an API to your application that will (most likely) causes state changes
- After a state change, they dispatch the state change as a
Redux Actionto theRedux Reducersto inform them that the application state has changed.
A Redux Action:
- Describes a state change that has ALREADY HAPPENED in the application
- Describes the state change to the
Redux Reducer
A Redux Reducer:
- Listens for state changes in your application; I.e. Listens for
Redux Actions - Inspects the current state and the action payload to determine what has changed
- Decides what changes it cares about based on it's inspections
- Keeps track of state changes by updating the Redux Store IF IT DECIDES TO DO SO!
- Does not mutate current state
- Always returns what it believes to be the current Application State
The wrong way is to think of Redux Actions as things we WANT REDUX TO DO FOR US. Redux Actions are NOT your API! Your Action Creators are!
Do not try to control Redux! Let it respond to you! Inform it of when things have changed, and let it (and it's Redux Reducers) decide how to keep track of things.
Using Redux Actions as a glorified getter/setter API on the global Redux store object is worse than just defining a global object and mutating it directly! Do not treat it as a global object!
The following example is how not to set things up.
- The
Action Creatorsare well defined. Good to go there... - The
Action Types,APPEND_TO_PRESET_LIST,SELECT_PRESET, andDESELECT_PRESET, lead us to believe that Redux will somehow do the work for us. That is a slippery slope to trating Redux as a global object. - The
Redux Reducersare given a task they shouldn't be doing. They are being told to update the state. They are not allowed to update the state based on what they know of the currentstateand theaction.payloadthey're being asked to respond to.
// Action Creators
export const saveToPresetsList = (fieldlist = [], name = 'preset_name') => {
return (dispatch, getState) => {
const {
presetData
} = getState()
return presetService
.create({
name,
presetData
})
.then(payload => dispatch({ type: APPEND_TO_PRESET_LIST, payload }))
}
}
export const selectPreset = (presetId) => {
return (dispatch, getState) => {
dispatch({
type: SELECT_PRESET,
payload: presetId
})
}
}
export const deselectPreset = () => {
return (dispatch, getState) => {
dispatch({
type: DESELECT_PRESET,
payload: -1
})
}
}
// Reducer that handles those Action Types:
function presetReducer(state = initialState, action) {
switch (action.type) {
case APPEND_TO_PRESET_LIST:
const availablePresets = state.availablePresets.concat(action.payload)
return {
...state,
availablePresets
}
case SELECT_PRESET:
return Object.assign({}, state, {
selectedPresetId: action.payload
})
case DESELECT_PRESET:
return Object.assign({}, state, {
selectedPresetId: -1
})
default:
return state
}
}This is mostly right, as far as I'm concerned.
- The
Action CreatorAPI is the same. No real difference as far as the App is concerned... - The
Action Typesdefine state changes that have already happened by the time Redux hears about it.- A
PRESET_CREATEDstate change has happened- The
PRESET_CREATEDRedux Reducerwill record the change
- The
- A
PRESET_SELECTION_CHANGEDstate change has happened- The
PRESET_SELECTION_CHANGEDRedux Reducerwill record the change
- The
- A
- The
Redux Reducersare free to respond to state changes based on the currentstateandaction.payloadas it learns about the state changes.
// Action Creators
export const saveToPresetsList = (fieldlist = [], name = 'preset_name') => {
return (dispatch, getState) => {
const {
presetData
} = getState()
return presetService
.create({
name,
presetData
})
.then(payload => dispatch({ type: PRESET_CREATED, payload }))
}
}
export const selectPreset = (presetId) => {
return (dispatch) => {
dispatch({ type: PRESET_SELECTION_CHANGED, payload: presetId })
}
}
export const deselectPreset = () => {
return (dispatch) => {
dispatch({ type: PRESET_SELECTION_CHANGED, payload: -1 })
}
}
// Reducer that handles those Action Types:
function presetReducer(state = initialState, action) {
switch (action.type) {
case PRESET_CREATED:
const availablePresets = state.availablePresets.concat(action.payload)
return {
...state,
availablePresets
}
case PRESET_SELECTION_CHANGED:
if (action.payload) {
return Object.assign({}, state, {selectedPresetIndex: action.payload})
}
return Object.assign({}, state, {selectedPresetIndex: -1})
default:
return state
}
}