Hey guys i am stuck in a situation in which i have to set the inital state of reducer to some value let me show you the code
First of all i have an action creater like this
export const fetchuser = () => {
return async dispatch => {
const res = await axios.get("/api/currentuser");
dispatch({
type: "fetchuser",
payload: res.data
});
};
};
which just fetches the data from api and dispatches an action to reducer
export default function(state = {}, action) {
switch (action.type) {
case "fetchuser":
return action.payload||false;
default:
return state;
}
}
now in second action creater i have to make a post request and increase the "credits" value in user database
export const handletoken = token => {
return async dispatch => {
const res = await axios.post("/api/stripe", token);
dispatch({ type: "credits", payload: res.data });
};
};
so i get the updated value here then i pass this on to the reducer
export default function(state = {}, action) {
switch (action.type) {
case "credits":
return action.payload
default:
return state;
}
}
and then combine them in reducer/index.js
export default combineReducers({
auth: authreducer,
credits:creditsreducer
});
console log of auth reducer in app.js in mapstatetoprops function gives
auth:
credits: 40
googleid: "109463598810933991924"
__v: 0
_id: "5d7fff2c4cb0604139055ce4"
so in credits reducer as u can see i have defined initial value of state as an empty object but i want to set it as the value of credits key of auth reducer, I could easily set it to array or an object hardcoding it but here i need to set its value as a value which is already in my another reducer so how can i achieve this ?
Assuming you need to wait for "fetchuser" to succeed to set credits in your creditsreducer you can handle the "fetchuser" action in your creditsreducer as well:
export default function(state = {}, action) {
switch (action.type) {
case "fetchuser":
return action.payload ? action.payload.credits : state;
case "credits":
return action.payload
default:
return state;
}
}
Always keep previous reducer state value. Otherwise no use of redux state value. like this
1.export default function(state = {}, action) {
switch (action.type) {
case "fetchuser":
let data = action.payload||false;
return {
...state,
fetchuser: data //any where you can access fetchuser data as well as previous state will not change.
}
default:
return state;
}
}
Change all the reducers like above.
Related
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
)
In redux actions, when we want to set a value, we use a type to dispatch like this :
dispatch({
type: SET_LOADER,
payload: true
})
Where the type: SET_LOADER stored in a different file and export it like below.
export const SET_LOADER = 'SET_LOADER'
And in reducer we will do it like this :
function initialState() {
return {
formErr: {},
isLoading: false
}
}
export default function (state = initialState(), action) {
const { type, payload } = action;
switch (type) {
case SET_LOADER:
return {
...state,
isLoading: payload
}
default:
return state
}
}
So in my application, I have this SET_LOADER type used in different actions and reducers. For example, in authentication, in profile update, when I want to load, I will use this type. So I have this type imported in various places.
I'm not sure if it's okay to use a single type for multipurpose because I noticed now that when I do dispatch, the redux state that get updated is not belonged to the target reducer. The state update is happening at different reducer.
But it's working for the first time dispatch. The next update, it's updating the incorrect redux state. After I refresh the page and try to update again, then it work.
first of all you need to separate your reducer into multiple reducers and then combine them in the store , then you can probably get away by using that same action in multiple cases for but then it'll be only a per reeducer solution meaning that let's say you have and Auth reducer this reducer will have its isLoading , and it may interfere with other actions within that reducer , fore example FetchAllProducts will use isLoading but also FetchByIdProduct is using isLoading and same for other actions that will trigger a loading state .
let's consider these reducers which use the same initial state
function initialState() {
return {
formErr: {},
isLoading: false
}
}
export const authReducer=(state = initialState(), action)=> {
const { type, payload } = action;
switch (type) {
case SET_LOADER:
return {
...state,
isLoading: payload
}
default:
return state
}
}
export const productsReducer=(state = initialState(), action)=> {
const { type, payload } = action;
switch (type) {
case SET_LOADER:
return {
...state,
isLoading: payload
}
default:
return state
}
}
export const cartReducer =(state = initialState(), action)=> {
const { type, payload } = action;
switch (type) {
case SET_LOADER:
return {
...state,
isLoading: payload
}
default:
return state
}
}
//this is the store
import {createStore,applyMiddleware,compose,combineReducers} from 'redux'
import thunk from 'redux-thunk'
import {productsReducer} from './reducers/ProductReducer'
import {cartReducer} from './reducers/CartReducer'
import {authReducer } from './reducers/AuthReducer'
const initialState={
products: {
formErr: {},
isLoading: false
},
cart: {
formErr: {},
isLoading: false
},
auth: {
formErr: {},
isLoading: false
}
}
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(combineReducers({
products: productsReducer,
cart : cartReducer ,
auth : authReducer,
}),
initialState,
composeEnhancer(applyMiddleware(thunk))
)
export default store
even though their using the same initial state you , when you will connect a component to the redux store you have access to three different isLoading :
export default connect((state)=>({
isLoading : state.products.isLoading,
isLoading2: state.authReducer.isLoading,
isLoading3: state.cart.isLoading,
}))(Products)
but to be honest I'd rather have make my actions more explicit and case specific something like productsFetchIsLoading , this gives you more control and prevents bugs
I noticed now that when I do dispatch, the redux state that get updated is not belonged to the target reducer. The state update is happening at different reducer.
Every action gets dispatched to every reducer. When you call dispatch({ type: SET_LOADER, payload: true }), the expected behavior is that the isLoading state will get set to true in every reducer which has a case SET_LOADER.
If you want the loading states to be independent then each reducer needs a unique string action type.
If you have multiple similar reducers then you can use a factory function to generate the type names, action creator functions, and reducer cases. Here we are extending the createSlice utility from Redux Toolkit.
We pass in the name which is the prefix for the auto-generated action types, the initialState of just the unique properties for this reducer state, and any unique reducer cases. This will get merged with the standard base state.
Helper:
const createCustomSlice = ({name, initialState = {}, reducers = {}}) => {
return createSlice({
name,
initialState: {
formErr: {},
isLoading: false
...initialState,
},
reducers: {
setLoader: (state, action) => {
state.isLoading = action.payload;
},
setFormErr: (state, action) => {
state.formErr = action.payload;
}
...reducers,
}
});
}
Usage:
const profileSlice = createCustomSlice({
name: "profile",
initialState: {
username: ""
},
reducers: {
setUsername: (state, action) => {
state.username = action.payload;
}
}
});
// reducer function
const profileReducer = profileSlice.reducer;
// action creator functions
export const { setFormErr, setLoader, setUsername } = profileSlice.actions;
These action creators will create actions with a prefixed type like 'profile/setLoader'.
populate my initialState with json api call from a component or from here
const initialState = {
myvalues: [] ---->here i want to populate this array
};
const reducer = (state = initialState, action) => {
const newState = { ...state };
switch (action.type) {
case "Update":
console.log(newState);
// newState.myvalues = action.key.title.value;
default:
return newState;
}
};
export default reducer;
To populate your initialState with data from an API, you can create e.g. an FETCH_INIT_DATA_ACTION, which get's dispatched right after you initialised your store.
// ...
const store = createStore(/* ... */)
store.dispatch({ type: 'FETCH_INIT_DATA_ACTION' })
// ...
whereas FETCH_INIT_DATA_ACTION triggers a redux-thunk, saga, effect or whatever you want to use.
You can do it like this:
const reducer = (state = initialState, action) => {
switch (action.type) {
case "Update":
return { ...state, myvalues: action.payload }
default:
return state;
}
};
And when you dispatch it you should put your API data in payload.
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 quite new to Redux and from what I understand, a reducer should be created for each type of object. E.g. for user interaction a user reducer should be created. My question is: How do you handle cases where you require the object for different purposes?
Scenario: Imagine having a user reducer which returns the current user. This user would be required in the entire application and needed for general controls on every page.
Now what happens when you need to load another user which is used for different purposes. E.g. profile page: loading a user to display information.
In this case there would be a conflict if the user reducer would be used. What would be the correct way to handle this in Redux? In case a different reducer would have to be created, what would be the naming convention for the new reducer?
First, you've mentioned:
a user reducer which loads the current user
I don't know if I got you correctly, but if this means you want to fetch (from an API, for example) the current user inside the reducer, this is a wrong approach.
Reducers are intended to be pure functions. You can call them with the same arguments multiple times and they will always return the same expected state.
Side effects like that should be handled by action creators, for example:
actions/user.js
export const FETCH_ME = 'FETCH_ME'
export const FETCH_ME_SUCCESS = 'FETCH_ME_SUCCESS'
// it's using redux-thunk (withExtraArgument: api) module to make an async action creator
export const fetchMe = () => (dispatch, getState, api) => {
dispatch({ type: FETCH_ME })
return api.get('/users/me').then(({ data }) => {
dispatch({ type: FETCH_ME_SUCCESS, data })
return data
})
}
Inside your reducer you can simple get the data and set a new state (note that if you send the action with the same data multiple times, the state will always be the same).
reducers/user.js
import { FETCH_ME, FETCH_ME_SUCCESS } from '../actions/user'
const initialState = {
item: null,
loading: false
}
export const userReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_ME:
return {
...state,
loading: true
}
case FETCH_ME_SUCCESS:
return {
...state,
loading: false,
item: action.data
}
default:
return state
}
}
Now, for your scenario:
Now what happens when you need to load another user which is used for different purposes. E.g. profile page: loading a user to display information.
You will just write another action creator for that:
actions/user.js
export const FETCH_ME = 'FETCH_ME'
export const FETCH_ME_SUCCESS = 'FETCH_ME_SUCCESS'
export const FETCH_USER = 'FETCH_USER'
export const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS'
export const fetchMe = () => (dispatch, getState, api) => {
dispatch({ type: FETCH_ME })
return api.get('/users/me').then(({ data }) => {
dispatch({ type: FETCH_ME_SUCCESS, data })
return data
})
}
export const fetchUser = (id) => (dispatch, getState, api) => {
dispatch({ type: FETCH_USER })
return api.get(`/users/${id}`).then(({ data }) => {
dispatch({ type: FETCH_USER_SUCCESS, data })
return data
})
}
Then you adapt your reducer to manage more sets:
reducers/user.js
import { combineReducers } from 'redux'
import { FETCH_ME, FETCH_ME_SUCCESS, FETCH_USER, FETCH_USER_SUCCESS } from '../actions/user'
const initialState = {
item: null,
loading: false
}
const meReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_ME:
case FETCH_ME_SUCCESS:
return userReducer(state, action)
default:
return state
}
}
const activeReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_USER:
case FETCH_USER_SUCCESS:
return userReducer(state, action)
default:
return state
}
}
const userReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_USER:
case FETCH_ME:
return {
...state,
loading: true
}
case FETCH_USER_SUCCESS:
case FETCH_ME_SUCCESS:
return {
...state,
loading: false,
item: action.data
}
default:
return state
}
}
export default combineReducers({
activeUser: activeReducer,
me: meReducer
})
Your final user state should be something like:
{
me: {
item: null,
loading: false
},
active: {
item: null,
loading: false
}
}