Redux mutations boolean - redux

This might be a silly questions, but i'm using Redux for an React application, and i need to store some application state as boolean values.
When im picking up an action in a reducer, i need to set the boolean to the opposit of the current. Currently im doing this with:
export default function(state = true, action) {
switch (action.type) {
case 'SOME_ACTION_TYPE':
return action.payload;
}
return state;
}
The action.payload already holds the correct value, but can i just assign it as im doing now, or am i mutating the state? Can you even mutate it as it's a boolean?
Thanks

You should try:
export default function(state = true, action) {
switch (action.type) {
case 'SOME_ACTION_TYPE':
return !state;
}
return state;
}
Hope it can help

Related

Redux call function on change

I started using Redux with React. My Store contains several job Objects each with an id. I want to call a function, whenever I add/update a new Job. What's the recommended way to do so?
Here's my reducer
function reducer(state:= {}, action) {
const { id, fields } = action.payload
switch (action.type) {
case ADDED:
if (!state[id]) {
return {
...state,
[id]: fields,
},
};
} else {
const currentDate = moment(stat[id].lastModified)
const newDate = moment(fields.metaData.lastModified);
if (!currentDate.isSame(newDate)) {
return {
...state,
[id]: { ...state[id], ...fields },
},
};
}
// don't update otherwise
return state;
}
case OTHERS:
......
}
Whenever something is added, I add it to the state, if it already exists but the modificationDate is newer, I update it in the state. I want my function to the called in either ADDED cases
Reducers must do only state update (no more). It seems you need using Redux middleware for that

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?

Reducers in redux starting to look redundant - any way to combine them in a nicer way?

Using redux, I have a bunch of actions and a bunch of reducers that coincide with each of the action types.
Each of the actions map to a different part of the state being updated (all the action creators are primarily for fetching data from an API, for example, which maps to some part of the state). Our reducers currently look rather silly (simplified to A, B, C, for the sake of example):
export const rootReducer = combineReducers({
somePartOfStateA: someAReducer,
somePartOfStateB: someBReducer,
somePartOfStateC: someCReducer,
... (and many other reducers)
});
function someAReducer(state = [], action) {
switch (action.type) {
case FETCH_SOME_A:
return action.payload;
default:
return state;
}
}
function someBReducer(state = [], action) {
switch (action.type) {
case FETCH_SOME_B:
return action.payload;
default:
return state;
}
}
function someCReducer(state = [], action) {
switch (action.type) {
case FETCH_SOME_C:
return action.payload;
default:
return state;
}
}
// and a bunch of pretty much identical reducers continue below
From my understanding, the purpose of having them split is so that each reducer handles a part of the state's namespaces. The resulting reducers are simple, but pretty much the same thing over and over. Is there a recommended way to consolidate all of these reducers per piece of state?
Reducer is just a function. You could use higher order function to make reducer for you.
const makeSingleActionReducer = type => (state, action) =>
action.type === type ? action.payload : state
export const rootReducer = combineReducers({
somePartOfStateA: makeSingleActionReducer(FETCH_SOME_B)
...
})
Also you could go further by creating a config {stateKey: actionType, ...} and loop over it.

Redux creates state with double nesting

I have a redux app that, amongst other things, updates a single string, hence the state could be reflected as:
{
theDataString: "someString",
otherData: { ...some other data... },
someListItems: [ ...a data array... ]
}
Hence I have the following reducer:
function updateDataString(state = {}, action) {
switch (action.type) {
case UPDATE_DATA_STRING:
return Object.assign({}, ...state, {theDataString: action.theDataString});
default:
return state;
}
}
However, when dispatch the UPDATE_DATA_STRING action, the theDataString value in the state becomes double nested:
{
theDataString: {theDataString: "someString"},
otherData: {... some other data... },
someListItems: [ ... a data array ... ]
}
This same problem has already been encountered here. However, the solution for them was that they were calling combineReducers on a single reducer when that was unnecessary. In my case, I'm calling combineReducers on multiple reducers - so their answer doesn't work for me. Also note that the same nesting problem does not occur for other data, only the top-level string gets double nested.
What is going wrong here?
EDIT:
I'm connecting the component that updates theDataString like this:
import {connect} from 'react-redux';
import {updateDataString} from './actions/actions';
import SomeList from './components/someList';
const mapStateToProps = (state) => {
return {someListItems: state.someListItems}
};
const mapDispatchToProps = (dispatch, placeHolder) => {
return {
updateDataString: (aString) => dispatch(updateDataSting(aString))
}
};
export default SomeListConnected = connect(mapStateToProps, mapDispatchToProps)(SomeList)
The action is set up as follows:
export const UPDATE_DATA_STRING = 'UPDATE_DATA_STRING';
export function updateDataString(aString) {
return {type: UPDATE_DATA_STRING, theDataString: aString}
}
EDIT2: Changing the way the reducer updates the state is a natural place to look for answers. However I have tried various permutations here with little effect:
Object.assign({}, ...state, {theDataString: action.theDataString});
Object.assign({}, state, {theDataString: action.theDataString});
{...state, {theDataString: action.theDataString}};
None of the above fix the problem.
Assuming that you've used combineReducers, your updateDataString reducer should be treating the state as a string, not an object:
function updateDataString(state = "", action) {
switch(action.type) {
case UPDATE_DATA_STRING: return action.newString;
default; return state;
}
}
The slice reducer will only see the string value as its "state", so you need to treat it that way.
See Using combineReducers for some further information on the topic.
Try this
return { ...state, theDataString: action.newString};
instead of this
return Object.assign({}, ...state, {theDataString: action.newString});
in your reducer.
I think you are using ... spread operator in the wrong way. And no need to use Object.assign() here.
Read more about spread operator here

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