I'm building a quite simple app using Redux and my reducers all look alike. It works, technically, but that's a lot of code duplication.
// The employees reducer
export default (state = initialState, action) => {
switch (action.type) {
case EMPLOYEES_REQUEST:
return [ ...state, { isFetching: true } ]
case EMPLOYEES_SUCCESS:
// DEBUG
console.log('Dispatching employees');
console.log(action.response);
// END DEBUG
// Return employees directly in the employees state
return { ...state, list: JSON.parse(action.response) };
case EMPLOYEES_FAILURE:
return [ ...state, { isFetching: false } ]
default:
return state
}
}
And
// The services reducer
export default (state = initialState, action) => {
switch (action.type) {
case SERVICES_REQUEST:
return [ ...state, { isFetching: true } ]
case SERVICES_SUCCESS:
// DEBUG
console.log('Dispatching services');
console.log(action.response);
// END DEBUG
// Return services directly in the services state
return { ...state, list: JSON.parse(action.response) };
case SERVICES_FAILURE:
return [ ...state, { isFetching: false } ]
default:
return state
}
}
Is there something I can to to use a generic reducer with different actions?
Thanks!
Reducer is just a function. You could always use a higher order function to make it.
const makeListReducer = (initial, prefix) => (state = initial, action) => {
switch(action.type) {
case `${prefix}_REQUEST`: return {...state, isFetching: true}
case `${prefix}_SUCCESS`: return {...state, isFetching: false, list: JSON.parse(action.response)}
case `${prefix}_FAILURE`: return {...state, isFetching: false, /*etc*/}
}
return state
}
// The employees reducer
export default makeListReducer(initialState, 'EMPLOYEES')
Related
Any idea why my reducer is acting on action.type based on its position in the switch statement?
I spent a lot of time debugging and then realized it was actually the position in the switch statement that is causing the problem.
import {
FETCH_PRODUCTS_BEGIN,
FETCH_PRODUCTS_SUCCESS,
FETCH_PRODUCTS_ERROR,
CREATE_PRODUCT,
UPDATE_PRODUCT,
DELETE_PRODUCT,
} from "./inventoryTypes";
const initialState = {
loading: false,
products: [],
error: "",
};
const userInventoryReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_PRODUCTS_BEGIN:
return {
...state,
loading: true,
};
case FETCH_PRODUCTS_SUCCESS:
return {
loading: false,
products: action.payload,
error: "",
};
case FETCH_PRODUCTS_ERROR:
return {
loading: false,
products: [],
error: action.payload,
};
case DELETE_PRODUCT:
return {
...state,
products: state.products.filter(
(product) => product._id !== action.payload
),
};
case CREATE_PRODUCT:
console.log("your calling create!");
return {...state}
case UPDATE_PRODUCT:
console.log("calling update");
return { ...state };
default:
return state;
}
};
Check the values inside your "./inventoryTypes" file' maybe there are duplications of the same actions.
This is resolved. The value for my CREATE_PRODUCT, UPDATE_PRODUCT inside "./inventoryTypes" is the same!
I've been working with redux for the last couple weeks and was incorporating it into my projects when I ran into this wall. Pretty common reducer for modals being rendered so i can animate them before unmounting them.
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
switch (action.type) {
case "modalInteraction":
return {
isModalOpen: action.payload
};
case "testModalInteraction":
return {
test: action.payload
};
default:
return state;
};
}
Sadly, the test property is still returning as undefined despite the fact that the other initial state in the same reducer can be called without a problem. I even removed all the testModalInteraction dispatches in the case that that somehow upset the datatype. I just can't spot the difference that keeps returning undefined.
When you return the new state, make sure to spread the initial state (...state) and then change whatever values you need to change.
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
switch (action.type) {
case "modalInteraction":
return {
...state,
isModalOpen: action.payload
};
case "testModalInteraction":
return {
...state,
test: action.payload
};
default:
return state;
};
}
If it is still undefined, make sure the payloads are defined for both actions.
For example, your modalInteraction action could look like
export const modalInteraction = (bool) => ({
type: "modalInteraction",
payload: bool
})
P.S., you can destructure the action object. This allows you to use "type" instead of "action.type" and "payload" instead of "action.payload".
const initialState = {
isModalOpen: false,
test: false
}
export default function(state = initialState, action) {
const {type, payload} = action;
switch (type) {
case "modalInteraction":
return {
...state,
isModalOpen: payload
};
case "testModalInteraction":
return {
...state,
test: payload
};
default:
return state;
};
}
I would like to give more flexibility to my reducer without adding different initalstates and
cases.
I try to explain:
import {
FETCH_DB,
FETCH_CAT,
} from "../types"
// put a variable here:
const initalState = {
db: [],
categories: [], // create a dynamic state i.e. categories.VARIABLE
}
export default (state = initalState, action) => {
switch (action.type) {
case FETCH_DB:
return {
...state,
db: action.payload,
current: null,
loading: false,
}
case FETCH_CAT:
VARIABLE HERE
return {
...state,
categories[VARIABLE]: action.payload, // syntax with [] doesn't work
loading: false,
}
default:
return state
}
}
My aim is to pass a variable through out a component so I can have categories.VAR as many as I want.
Does someone know if is possible?
Thanks!
I guess it may be your solution 🙂
case FETCH_CAT:
const newState = {
...state,
loading: false
}
newState.categories[variable] = action.payload
return newState
Do
case FETCH_CAT:
return {
...state,
categories: {
...state.categories,
[variable]: action.payload
},
loading: false
}
Below is the code of my reducer. whenever I am trying to update list array it says it is not iterable. Can anyone please tell me what mistake I am doing?
initialState = {
list: [],
taskAdded: false,
taskList: ""
}
export const task = (state = initialState, action) => {
switch (action.type) {
case types.ADD_TASK:
return {
...state,
taskList: action.payload,
taskAdded: true,
list: [...state.list, action.payload]
}
case types.CLEAR_ADD_TASK_STATE:
return {
...state,
taskList: "",
taskAdded: false
}
default:
return state
}
}
When I try to call any other action in redux it is setting one part of the state to it's initialState.
My root reducer looks like this
const rootReducer = combineReducers({
credentials: combineReducers({
cred,
user,
partner,
merchant,
bank,
error,
auth,
}),
preCredentials,
theme,
});
the part of the state that is being cleared is theme.
action dispatched
state diff
Why this actions that have anything to do with theme reducer can change its state.
Theme reducer
function theme(state = { ...INITIAL_THEME }, action) {
switch (action.type) {
case LOADING_THEME:
return {
...state,
isLoading: true,
};
case SAVE_THEME:
return {
...action.theme,
error: {
status: null,
message: '',
},
isLoading: false,
};
case CLEAR_THEME:
return INITIAL_THEME;
default:
return INITIAL_THEME;
}
}
reducer of dispatched action
function preCredentials(state = { ...INITIAL_STATE }, action) {
switch (action.type) {
case SAVE_USERNAME:
return { ...state,
user: { ...state.user,
fullName: action.fullName,
},
};
default:
return state;
}
}
function theme(state = { ...INITIAL_THEME }, action) {
switch (action.type) {
case LOADING_THEME:
return {
...state,
isLoading: true,
};
case SAVE_THEME:
return {
...state,
...action.theme,
error: {
status: null,
message: '',
},
isLoading: false,
};
case CLEAR_THEME:
return INITIAL_THEME;
default:
return state;
}
}
return state instead of initial state