Currently I have to call three different api end points to get the information for some dropdown in a redux form.
The question I have is atm to get the end points into separate sets of state. I currently have three different reduces being imported into the index reducer so I can have them under three state terms. I have nearly the same code in three files:
//reducer/job-board/job_tools
import {FETCH_JOB_TOOLS} from "../../actions/job_list_actions";
export default function(state = {}, action) {
switch(action.type) {
case FETCH_JOB_TOOLS:
return action.payload.data;
default:
return state;
}
}
////reducer/job-board/job_roles
import {FETCH_JOB_ROLES} from "../../actions/job_list_actions";
export default function(state = {}, action) {
switch(action.type) {
case FETCH_JOB_ROLES:
return action.payload.data;
default:
return state;
}
}
Seeing as this code is nearly identical is there a way for me to dry this up and still keep three separate pieces of s
Pleas help.
Thank you.
You can do a FETCH_JOB action and pass job_type from action parameter. So you'll have one unique fetch function in reducer and you'll provide what you're fetching, this should be the key in state for what you're fetching.
store = { roles: [], tools: [] }
your reducer will looks like:
import {FETCH_JOB} from "../../actions/job_list";
export default function(state = {}, action) {
switch(action.type) {
case FETCH_JOB:
return {
...state,
[action.job_type]: action.payload.data
}
default:
return {...state};
}
}
note: in this case, it's important to generate a new state with { ...state, [...] } instead of just mutate the previous one.
By the way, I am using an array like syntax instead of switch case.
you can use a function to create reducers and provide array containing handlers.
import createReducer from './createReducer';
const loginReceived = (state, action) => ({
...state,
authData: action.auth
});
// [...]
const ACTION_HANDLERS = {
[AUTH_LOGIN_START]: loginStarted,
[AUTH_LOGIN_COMPLETE]: loginReceived,
[AUTH_LOGIN_FAIL]: loginFail
};
export default createReducer(initialState, ACTION_HANDLERS);
and use this as createReducer function
/**
* Creates a reducer.
* #param {string} initialState - The initial state for this reducer.
* #param {object} handlers - Keys are action types (strings), values are reducers (functions).
* #return {object} A reducer object.
*/
export default (initialState = null, handlers = {}) => (state = initialState, action) => {
if (!action && !action.type) return state;
const handler = handlers[action.type];
return handler && handler(state, action) || state;
};
Related
I am having a two step form in react. The first step of the form we ask some information to the user and then I add it to the state. The second step of the form I ask some more information to the user and add it to the state, so instead of appending the information that was asked on step 2 of the form, it overrides the state, so the state now only the info that was asked in the step 2 of the form. How can I add the have both the information together. When i try to ...state it gives me error as state is not iterable.
const infoReducer = (state = {}, action) => {
switch(action.type) {
case 'STEP_1':
return action.payload
case 'STEP_2':
return action.payload
default:
return state
}
}
check this out
import { ActionTypes } from "../../Constant/ActionType";
const initState = {
Auth: {},
};
const AuthReducer = (state = initState, action) => {
switch (action.type) {
case ActionTypes.AuthUser:
return {
...state,
Auth: action.payload,
};
default:
return state;
}
};
export default AuthReducer;
first, copy the current state data and add a new action payload as second parameter
case 'ADD_Data':
return {...state,action.payload}
I have a product filter array which i stored in redux, initially its an empty array and i want to dispatching an object in it one by one .
The issue is i want to check if the object_id is already is in the array then i don't want to dispatch it and as well as delete that particular object in redux state.
Likewise,
if user added blue color filter so its accept but if user again added blue color filter so its shouldn't dispatch this object and also remove this object in redux.
reducer:
filterd :[]
const filterreducer = (state = initialState, action) => {
switch (action.type) {
case ADD_FILTER:
return {
...state,
filterd :[...state.filterd,action.payload]
};
default:
return state;
}
}
export default filterreducer ;
initialState ={
filterd :[]
}
const filterreducer = (state = initialState, action) => { switch (action.type) {
case ADD_FILTER:
return {
...state,
filterd :action.paylod
};
default:
return state;
}
}
export default filterreducer ;
you have to store response data in payload while dispatching that action
such as:
dispatch({
type:ADD_FILTER,
payload: response.data
)
I have been playing with react-native and redux and I have encountered an error. The state is always initialised as the starting one although the payload is present when I debug it inside the reducer.
This is my reducer file
let initialState = {
filterList: [],
isFetching: false,
activeFilters: [],
}
export function fetchFilterList(state = initialState, action) {
return { ...state, isFetching: true };
}
export function fetchFilterListSuccess(state, action) {
return {
...state,
filterList: action.payload,
isFetching: true,
dsad: "dada",
}
}
export function fetchFilterListError(state, action) {
return { ...state, isFetching: false };
}
This is where I combine them into one function(in the same file as above):
export function combinedFiltersReducers(state = initialState, action) {
switch (action.type) {
case ACTION_TYPES.FETCH_FILTER_LIST:
return fetchFilterList(state, action);
case ACTION_TYPES.FETCH_FILTER_LIST_SUCCESS:
return fetchFilterListSuccess(state, action);
case ACTION_TYPES.TOGGLE_FILTER_ITEM:
return toggleFilterItemStart(state, action);
case ACTION_TYPES.TOGGLE_FILTER_ITEM_SUCCESS:
return toggleFilterItemSuccess(state, action);
default:
return state;
}
}
This is my combine reducers function in a separate file called main reducers.
export default combineReducers({
adList: fetchAdListSuccess,
filterList: combinedFiltersReducers,
});
Here is the where I receive the state in the component, and it always falls to the initial state.
const mapStateToProps = state => ({
filterList: state.filterList,
});
const mapPropsToDispatch = dispatch => ({
fetchFilterList:() => dispatch(fetchFilterList()),
toggleFilterItem: (data) => dispatch(toggleFilterItem(data)),
});
export default connect(
mapStateToProps,
mapPropsToDispatch
)(FilterComponent);
I can not find the error, so I need a bit of help. Thanks in advance.
Okay, I have found an issue. Taking an example:
return fetchFilterList(state, action);
You're calling fetchFilterList method and passing initial state. So every time you call it actually passes the initial state. And that method is just copying the initial state. Rather do like this:
return fetchFilterList(...state, action);
I am going to assume that your ACTION_TYPES do not have the appropriate type(s) for the function you are calling so it is reverting to the default case.
I'm trying to delete an element from dom by clicking on it. I did it without the problem without redux thunk but now I have a problem. My reducer doesn't know about the state. How do let him know what items are?
Action:
export function deleteItem(index) {
return {
type: 'DELETE_ITEM',
index
};
}
My reducer that shows undefined.
export function deleteItem(state = [], action) {
switch (action.type) {
case 'DELETE_ITEM':
const copy = state.items.slice()
console.log(copy)
default:
return state;
}
}
Heres my actual code https://github.com/KamilStaszewski/flashcards/tree/develop/src
I saw your code and you are defining a new reducer for each of the operations you want to get done to your items (e.i itemsHaveError, deleteItem, ...) but the correct way of doing this is to store all of the relevant functions for the items to a single reducer which holds the data needed to change whenever some action to the items happens, but in the way you did it, any time any action happens because your reducers are separated the initial state gets empty as you have passed to the functions and the reducers do not know about their related data so they overwrite them with the empty initial state, the correct way would be like this to write a single reducer for items:
const initialState = {
isLoading: false,
hasError: false,
items: [],
};
export default function(state = initialState, action) {
switch (action.type) {
case ITEMS_HAVE_ERROR:
return {
...state,
hasError: action.hasError,
};
case ITEMS_ARE_LOADING:
return {
...state,
isLoading: action.isLoading,
};
case ITEMS_FETCH_DATA_SUCCESS:
return {
...state,
items: action.items,
};
case DELETE_ITEM:
const copy = state.items.slice()
return {
...state,
items: copy,
};
default:
return state;
}
}
so this would be your item.js and your item reducer and the only one that should get to combineReducer function.
Indicate the initial State of the reducer by default , the state is an empty array and you can't access the state.items , cause it is undefined. Assume this:
const x = [];
x.foo.slice();
that would return an error . Thus from :
state = []
change it to :
state = {
items:[]
}
applying it to your code:
export function deleteItem(
state = {
items:[]
},
action) {
switch (action.type) {
case 'DELETE_ITEM':
const copy = state.items.slice()
console.log(copy)
default:
return state;
}
}
I have two reducers, the first one fetches data and the second one is used to filter the data based on changes to the original data. I have read that using combineReducers is not an option.
My postReducer
import {FETCH_POST} from '../actions/types';
const initialState = {
items: [],
item: {}
};
export default function(state = initialState, action){
switch (action.type){
case FETCH_POST:
return Object.assign({}, state, {items: action.payload});
default:
return state;
}
}
My pinReducer
import {PIN_POST} from '../actions/types';
const initialState = {
items: [],
item: {}
};
export default function(state = initialState, action){
switch (action.type){
case PIN_POST:
console.log(state.items);
const item = state.items.map(value => value.data.id === action.id ?
{data: Object.assign({}, value.data, {pinned: action.val})} : value
);
//console.log(item);
return Object.assign({}, state, {items: item });
default:
return state;
}
}
Main
import {combineReducers} from 'redux';
import postReducer from './postReducer';
import pinReducer from './pinReducer';
export default combineReducers({
post: postReducer,
pin: pinReducer
});
How can I share the state between the two reducers, as state.items in the pinReducer is empty
You should not have a reducers that has state derived on the basis of other reducers. What you need in your case is a selector(more efficiently a memoizedSelector), for which you can use reselect library
My postReducer
import {FETCH_POST} from '../actions/types';
const initialState = {
items: [],
item: {}
};
export default function(state = initialState, action){
switch (action.type){
case FETCH_POST:
return Object.assign({}, state, {items: action.payload});
default:
return state;
}
}
Main
import {combineReducers} from 'redux';
import postReducer from './postReducer';
export default combineReducers({
post: postReducer,
});
and then where you want to use pinItem, you can do that in mapStateToProps
getPinItem = (state, props) => {
const item = state.items.map(value => value.data.id === action.id ?
{data: Object.assign({}, value.data, {pinned: action.val})} : value
);
return item;
}
const mapStateToProps = (state, props) => {
const pinItem = getPinItem(state, props);
return {
pinItem
}
}
Reducer does not access to store's state, nor other reducers' previous state. Only to its own previous state and dispatched action. You can think of it as a single brick in the wall of redux' state.
In your case, we have to tackle the logic and decide what is an actual state here.
Requirements:
fetch a list of posts: state is an array of posts;
pin one of these posts: state is an id of currently pinned post;
get currently pinned post: find a post in an array by pinned post id.
Solution:
1 - initial posts reducer:
const initialState = { items: [] }
const reducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_POSTS:
return { items: action.payload }
default:
return state
}
}
2 - add info about pinned post:
const initialState = { items: [], pinnedId: null }
const reducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_POSTS:
return { ...state, items: action.payload.posts }
case PIN_POST:
return { ...state, pinnedId: action.payload.id }
default:
return state
}
}
3 - get a currently pinned post:
// assuming that store is configured...
export default combineReducers({ posts: postsReducer })
// ...
// somewhere in `connect`
const mapStateToProps = (state, ownProps) => {
return {
pinnedPost: state.posts.find(post => post.id === state.posts.pinnedId),
}
}
So now we store a minimum information about a pinned post (only its id) and it is enough to get an actual pinned post.
Also, as mentioned in another answer by #shubham-khatri, you can use optimized selectors. With current shape of the state storing pinned id separately, it is even more efficient since your selector will depend on two inputs: posts array (is re-created on fetch, so shallow comparable) and pinned id (is a primitive number or string, also easy to compare).
I'd encourage you to store as minimum information in store as possible. Also, store it in simple and flat shape. Move everything that's can be calculated to selectors.