why state.payload is undefined in reducer - redux

why state is returned empty
i had tried many solutions from this website but non of them worked
const initialState = [];
export default function(state = initialState, actions) {
const { type, payload } = actions;
switch (type) {
case DASHBOARD:
return [...state,payload]
default:
return state;
}
}
when this state is mapped to props , payload become undefined.
here when i console log action.payload it is defined but could not be passed in return statement.

May I know for which case it failing?
For Dashboard case,it will be "DASHBOARD" until and unless it is not constant.
And for your default case,you need to change your initial state like this:
const initialState = {payload:"your initial payload",your_other_properties:"here"};
export default function(state = initialState, actions) {
const { type, payload } = actions;
switch (type) {
case "DASHBOARD":
return [...state,payload]
default:
return state;
}
}

Related

Redux initial state causes undefined error

I am following an udemy course to learn redux coding on codePen, however, I got this following error.
redux.min.js:1 Uncaught TypeError: Cannot read property 'name' of undefined
at policies (pen.js:61)
at redux.min.js:1
at Array.forEach (<anonymous>)
at redux.min.js:1
at e.combineReducers (redux.min.js:1)
at pen.js:73
it tells me that it is because the name from the policies reducer function could not read the action.payload.name because it's undefined. I did not see the instructor have the same problem, but I have faithfully checked my code and it's identical to his. I just can't understand how I can fix it to display the state correctly.
I have debugged and tried to fix the problem, but so far the code keeps throwing error unless I delete the action.payload.name from the policies reducer function.
console.clear();
// people dropping off a form (Aaction Creator)
const createPolicy =(name, amount)=>{
// console.log(name,amount,'test createpolicy')
return { // Action (a form in our analogy)
type:'CREATE_POLICY',
payload: {
name:name,
amount: amount
}
};
};
const deletePolicy = (name) =>{
return {
type:'DELETE_POLICY',
payload:{
name: name
}
}
}
const createClaim = (name, amountOfMoneyToCollect) =>{
return {
type:'CREATE_CLAIM',
payload:{
name: name,
amountOfMoneyToCollect: amountOfMoneyToCollect
}
};
};
// Reducers (Departments!)
// default oldListOfClaims=[] is for initial state.
const claimsHistory=(oldListOfClaims=[], action)=>{
if(action.type==="CREATE_CLAIM"){
// we care about this action (FORM!)
return [...oldListOfClaims, action.payload];
}
// we don't care the action (FORM!)
return oldListOfClaims;
};
const accounting=(bagOfMoney=100, action)=>{
if(action.type==='CREATE_CLAIM'){
return bagOfMoney - action.amountOfMoneyToCollect;
}
else if(action.type==='CREATE_POLICY'){
return bagOfMoney + action.payload.amount;
}
return bagOfMoney;
}
const policies = ( listOfPolicies = [], action)=>{
if(action.type = 'CREATE_POLICY'){
console.log( action.payload,'test playload name')
return [...listOfPolicies,action.payload.name]
}
else if(action.type='DELETE_POLICY'){
return listOfPolicies.filter(name =>
name!==action.payload.name);
}
return listOfPolicies;
};
const { createStore, combineReducers} = Redux;
const ourDepartments = combineReducers ({
accounting: accounting,
claimsHistory: claimsHistory,
policies: policies
})
const store = createStore(ourDepartments);
console.log(store)
store.dispatch(createPolicy('Alex', 20));
store.dispatch(createPolicy('Jim', 30));
store.dispatch(createPolicy('Bob', 40));
console.log(store.getState());
I would expect to see the console.log of the current state after the new names and amounts are dispatched to the store. and figure out why this error keeps occurring.
Review your policies reducer - you are assigning rather than comparing against action types when checking against your types. Update it to the code below:
const policies = ( listOfPolicies = [], action)=>{
// previously if(action.type = 'CREATE_POLICY'){
if(action.type === 'CREATE_POLICY'){
console.log( action.payload,'test playload name')
return [...listOfPolicies,action.payload.name]
}
// previously else if(action.type='DELETE_POLICY'){
else if(action.type==='DELETE_POLICY'){
return listOfPolicies.filter(name =>
name!==action.payload.name);
}
return listOfPolicies;
};

redux - how to create a generic reducer?

In react-redux, I'm trying to create a generic reducer, meaning a reducer with common logic that writes (with that logic) each time to a different section in the store.
I read Reusing Reducer Logic over and over, I just can't wrap my head around it. Let's say I have this state:
{
a: { b: { c: {...} } } },
d: { c: {...} }
}
a and d are two reducers combined with combineReducers() to create the store. I want section c to be managed with common logic. I wrote the reducer logic for c, I wrapped it to create a higher-order reducer with a name.
How do I create the a reducer with the c reducer with reference to its location (and also d accordingly)? Maybe in other words, how do I create a reducer with a "store address", managing his slice of the state, agnostic to where it is?
I sure hope someone understands me, I'm new to redux and react.
Reducer are now simple function and can be reuse somewhere else
const getData = (state, action) => {
return {...state, data: state.data.concat(action.payload)};
};
const removeLast = (state) => {
return {...state, data: state.data.filter(x=>x !== state.data[state.data.length-1])};
}
Action type and reducer function are now declared in an array
const actions = [
{type: 'GET_DATA', reducer: getData},
{type: 'REMOVE_LAST', reducer: removeLast}
];
Initial state for the reducer
const initialState = {
data: []
}
actionGenerators creates an unique Id using Symbol and assign that Id to actions and reducer function.
const actionGenerators = (actions) => {
return actions.reduce((a,c)=>{
const id = Symbol(c.type);
a.actions = {...a.actions, [c.type]: id};
a.reducer = a.reducer ? a.reducer.concat({id, reducer: c.reducer}) : [{id, reducer: c.reducer}];
return a;
},{});
}
reducerGenerators is a generic reducer creator.
const reducerGenerators = (initialState, reducer) => {
return (state = initialState, action) => {
const found = reducer.find(x=>x.id === action.type);
return found ? found.reducer(state, action) : state;
}
}
Usage
const actionsReducerCreator = actionGenerators(actions);
const store = createStore(reducerGenerators(initialState, actionsReducerCreator.reducer));
const {GET_DATA} = actionsReducerCreator.actions;
store.dispatch({type: GET_DATA});
Checkout my github project where I have a working todo application utilizing this implementation.
Redux-Reducer-Generator

Redux state in reducer not up to date?

I'm building an application using React/Redux, I have an array of products which are loaded to Redux state asynchronously and from which I would then like to pull individual products. However, the reducer I have written to do this isn't working as it is registering state as null. This is confusing me as calling getState() in the thunk action creator prior to returning the action and triggering the reducer is logging the correct state with the array of products.
Is this an error in my code or simply part of how redux state updates?
ACTION CREATOR: getSingleProduct
export const getSingleProduct = productName => (dispatch, getState) => {
const action = { type: 'GET_SINGLE_PRODUCT', productName };
if (!getState().products.length) return dispatch(getAllProducts())
.then(() => {
console.log('STATE IN ACTION CREATOR THEN BLOCK', getState());
return dispatch(action);
})
.catch(console.log);
else return action;
}
REDUCER: currentProduct
const currentProduct = (state = null, action) => {
switch (action.type) {
case 'GET_SINGLE_PRODUCT':
console.log('STATE IN REDUCER', state);
return state.products.filter(prod => prod.name.toLowerCase() === action.productName)[0];
break;
default:
return state;
}
}
Console Log Output
STATE IN ACTION CREATOR THEN BLOCK
{ basket: Array(0), products: Array(6), currentProduct: null }
STATE IN REDUCER
null
State is null cause you defined it as null on first function call.
console.log state after action complete and you see value run.
It is wrong to return modified state. Should return new state.
const currentProduct = (state = null, action) => {
switch (action.type) {
case 'GET_SINGLE_PRODUCT':
console.log('STATE IN REDUCER', state);
const products = state.products.slice().filter(prod => prod.name.toLowerCase() === action.productName)[0];
return { ...state, products }
break;
default:
return state;
}
}
Reducer state was in fact up to date, the issue was a misunderstanding of how reducer state works. I was trying to utilise a dependent state which is not available from the state argument of the reducer. The resolution for me was to pass this information from a dependent state in on the action object.
action
export const getSingleProduct = (productName, products = []) => (dispatch, getState) => {
let action = {
type: 'GET_SINGLE_PRODUCT',
productName,
products: getState().products
}
if (!action.products.length) dispatch(getAllProducts())
.then(() => {
action = Object.assign({}, action, { products: getState().products });
dispatch(action);
})
.catch(console.log);
else return action;
}
reducer
const currentProduct = (state = {}, action) => {
switch (action.type) {
case 'GET_SINGLE_PRODUCT':
const currentProduct = action.products.filter(prod => prod.name.toLowerCase() === action.productName)[0];
return Object.assign({}, state, currentProduct);
default:
return state;
}
}

combine two redux reducers

I have a use case like this:
eventListReducer: will get a list of events based on date range
eventDetailReducer: will get the event details based on one event id
I know how to do the two above, my question:
When my page loads initially, I will get a list of events based on default date range and load the first event details, I can certainly create an
EventListAndDetailReducer to duplicate eventListReducer and eventDetailReducer. Is there any better way I can reuse the logic?
What I want to achieve is to have another action, that will first call getEvents and update the eventLists state, and then grab the first event and call setEvent and update the eventDetail state.
This is my eventDetailReducer:
const initialState = {
eventDetails: "",
}
const eventReducer = (state = initialState, action) => {
switch (action.type) {
case "SET_EVENT":
state = {
...state,
eventDetails: action.payload
};
break;
}
return state;
}
export default eventReducer;
This is my eventsReducer:
const initialState = {
eventsList: [],
}
//getEventsReducer
const getEventsReducer = (state = initialState, action) => {
switch (action.type) {
case "GET_EVENTS":
state = {
...state,
eventList: ["Joe", "Tom", "Marry"] //assuming this from some other endpoint
};
break;
}
return state;
}
export default getEventsReducer;
What about using EventListAndDetailReducer?
const initialState = {
eventsList: [],
eventDetails: ""
}
export function eventListAndDetailReducer(state, action) {
switch(action.type) {
case GET_EVENTS:
return {...state, eventList: eventsReducer(state.eventsList, action)}
case "SET_EVENT":
return {...state, eventDetails: eventDetailsReducer(state.eventDetails, action)}
default:
return state
}
}
and then somewhen start using combineReducers?
Why not just have the eventDetails reducer also update on the GET_EVENTS action?
const eventReducer = (state = initialState, action) => {
switch (action.type) {
case "SET_EVENT":
state = {
...state,
eventDetails: action.payload
};
break;
case "GET_EVENTS":
state = {
...state,
eventDetails: action.payload[0] // assuming payload is an array
};
break;
}
return state;
}
Remember, all reducers receive all actions, so it does not need to be a 1-1 mapping.
What I understand from you question is that you want another action to do both actions sequentially and be dependent on each. I assume you have some middle ware such as redux-thunk that allows actions to be more than plaIn functions!
export function combinedAction() {
return (dispatch, getState) => {
// Write fetch() request to get events list from anywhere.
// Following should be within .then() if you're using fetch.
// Here events are just hardcoded in reducer!
dispatch(return { type: GET_EVENTS, payload: events }).then( () => {
let event = getState().eventsList[0]
dispatch(return { type: SET_EVENT, payload: event })
})
};
}
This will fire up GET_EVENTS action first and it'll set events array in state.eventsList. Then next action just uses this state information to dispatch next action SET_EVENT. Refer here to learn about chaining actions. How to chain async actions?

Issue with #ngrx/store and switch statements within reducers

I have the following two #ngrx/store reducers:
import {ActionReducer, Action} from '#ngrx/store';
import {UserAccount} from '../shared/models/useraccount.model';
export const SET_CURRENT_USER_ACCOUNT = 'SET_CURRENT_USER_ACCOUNT';
export const UPDATE_CURRENT_USER_ACCOUNT_FIRST_NAME = 'UPDATE_CURRENT_USER_ACCOUNT_FIRST_NAME';
export const currentUserAccountReducer: ActionReducer<UserAccount> = (state: UserAccount, action: Action) => {
console.log('currentUserAccountReducer:', state, action);
switch (action.type) {
case SET_CURRENT_USER_ACCOUNT: {
return action.payload;
}
case UPDATE_CURRENT_USER_ACCOUNT_FIRST_NAME: {
state.firstName = action.payload;
return state;
}
}
};
export const SET_AUTHENTICATED = 'SET_AUTHENTICATED';
export const SET_UNAUTHENTICATED = 'SET_UNAUTHENTICATED';
export const authenticatedReducer: ActionReducer<boolean> = (state: boolean, action: Action) => {
console.log('authenticatedReducer:', state, action);
switch (action.type) {
case SET_AUTHENTICATED: {
return true;
}
case SET_UNAUTHENTICATED: {
return false;
}
}
};
However, for some reason when I issue a dispatch for the 1st reducer (i.e. currentUserAccountReducer) then it changes the state for the 2rd reducer (i.e. authenticatedReducer)...
Here is the dispatch causing this issue:
this.store.dispatch({type: SET_CURRENT_USER_ACCOUNT, payload: currentUserAccount});
Here is how I initialize the store in the imports section:
StoreModule.provideStore(
{
currentUserAccount: currentUserAccountReducer,
authenticated: authenticatedReducer
})
Can someone please provide advice?
edit: The issue is that authenticated ends up undefined!!
The switch statements in your reducers do not contain default cases. You need to add default cases that return the state, as the reducers will be called for all actions - the store has no way of knowing which reducer should be called for a particular action type, so each dispatched action is passed to every reducer.

Resources