How would i convert this with usage of Immutable JS.
fromJS to wrap initial state and setIn method for nested cases.
const initialState = {
allMovies: []
};
const movieReducer = (state = initialState, action) => {
const {type, payload} = action;
switch (type) {
// some actions...
case movieActionTypes.FETCH_MOVIES_SUCCESS: {
return {
...state,
allMovies: {
...state.allMovies,
[payload.movie.id]: {
...state.allMovies[payload.movie.id],
...payload.movie,
ready: false,
},
},
};
}
default:
return state;
}
};
Related
Given the following 2 states as an example:
state_a: 'mode_1' | 'mode_2' | 'mode_3'
state_b: boolean
state_b can only be false whenever state_a === 'mode_2', and when state_a !== 'mode_2', state_b should return to the previous value (i.e. it's limits opened up. For example, if it was true -> false (limited) -> true or false -> false (limited) -> false).
What's the usual practice/style to define such a behavior in Redux?
If I'm understanding your question correctly you want, from the app's perspective, state_b to always be false when state_a === 'mode_2', and when state_a !== 'mode_2' state_b is whatever is stored in state.
On the surface your questions is posed in such a way that it sounds like you want to implement some logic in the reducer functions that coordinates the values between these two states when either of them update. While you could do this I suspect a simpler solution is to derive the provided state when consuming it. In other words, use a selector function to compute derived state, when the state_a value is "mode_2" then the selector function selecting state_b returns false, otherwise it returns the actual state_b state value.
Example:
import {
combineReducers,
configureStore,
createSlice,
createSelector
} from "#reduxjs/toolkit";
import { Provider, useDispatch, useSelector } from "react-redux";
const MODES = {
mode_1: "mode_1",
mode_2: "mode_2",
mode_3: "mode_3"
};
const state = createSlice({
initialState: {
state_a: MODES.mode_1,
state_b: true
},
name: "state",
reducers: {
setMode: (state, action) => {
state.state_a = action.payload;
},
toggleB: (state, action) => {
state.state_b = !state.state_b;
}
}
});
const { setMode, toggleB } = state.actions;
const selectState = (state) => state.state;
const select_a = createSelector([selectState], (state) => state.state_a);
const select_b = createSelector([select_a, selectState], (state_a, state) =>
state_a !== MODES.mode_2 ? state.state_b : false
);
Selecting the state in a component:
const state_a = useSelector(select_a);
const state_b = useSelector(select_b);
Demo:
You are looking for Sharing data between slice reducers,
import { combineReducers, createStore } from "redux";
type StateA = 'mode_1' | 'mode_2' | 'mode_3';
const stateAReducer = (state: StateA = 'mode_1', action) => {
switch (action.type) {
case 'SWITCH_MODE':
return action.payload;
default:
return state;
}
}
type StateB = boolean;
const stateBReducer = (state: StateB = true, action) => {
return state;
}
const combinedReducer = combineReducers({
stateA: stateAReducer,
stateB: stateBReducer
});
type CrossSliceState = ReturnType<typeof combinedReducer>;
const crossSliceReducer = (state: CrossSliceState, action) => {
switch (action.type) {
case 'SWITCH_MODE':
const nextStateA = stateAReducer(state.stateA, action);
return {
stateA: nextStateA,
stateB: nextStateA === 'mode_2' ? false : state.stateB
}
default:
return state;
}
}
const rootReducer = (state, action) => {
const intermediateState = combinedReducer(state, action);
const finalState = crossSliceReducer(intermediateState, action);
return finalState
}
const store = createStore(rootReducer);
store.subscribe(() => {
console.log(store.getState());
})
store.dispatch({ type: 'SWITCH_MODE', payload: 'mode_1' });
store.dispatch({ type: 'SWITCH_MODE', payload: 'mode_2' });
store.dispatch({ type: 'SWITCH_MODE', payload: 'mode_1' });
Output:
{ stateA: 'mode_1', stateB: true }
{ stateA: 'mode_2', stateB: false }
{ stateA: 'mode_1', stateB: false }
I have used created two actions and their respective reducers. When i dispatch any single action, both actions initial states are being saved to state where the parameters of the states are duplicated.
actions/index.js
import { COUNTER_CHANGE, UPDATE_NAVIGATION } from "../constants";
export function changeCount(count) {
return {
type: COUNTER_CHANGE,
payload: count,
};
}
export function updateNavigation(obj) {
return {
type: UPDATE_NAVIGATION,
payload: obj,
};
}
reducers.js
import { COUNTER_CHANGE, UPDATE_NAVIGATION } from "../constants";
import logger from "redux-logger";
const initialState = {
count: 0,
navigation: {},
};
export const countReducer = (state = initialState, action) => {
switch (action.type) {
case COUNTER_CHANGE:
return {
...state,
count: action.payload,
};
default:
return state;
}
};
export const updateNavigation = (state = initialState, action) => {
switch (action.type) {
case UPDATE_NAVIGATION:
return {
...state,
navigation: action.payload,
};
default:
return state;
}
};
// export default countReducer;
reducer/index.js
import { countReducer, updateNavigation } from "../reducers/countReducer";
import { combineReducers } from "redux";
const allReducers = combineReducers({
countReducer,
updateNavigation,
});
export default allReducers;
Dispatching actions
componentDidMount = () => {
const { navigation } = this.props;
this.props.updateNavigation(navigation);
};
const mapDispatchToProps = (dispatch) => {
return { ...bindActionCreators({ changeCount, updateNavigation }, dispatch) };
};
As we can see here I have triggered only updateNavigation action. But it updates states with duplicate parameters in redux state as shown below
The expected o/p will be
countReducer : {count : 0}
updateNavigation : {navigation :{}}
The shape of state for each reducer is incorrect. See defining-state-shape docs and try this:
export const countReducer = (state = { count: 0 }, action) => {
switch (action.type) {
case COUNTER_CHANGE:
return {
...state,
count: action.payload,
};
default:
return state;
}
};
export const updateNavigation = (state = { navigation: {} }, action) => {
switch (action.type) {
case UPDATE_NAVIGATION:
return {
...state,
navigation: action.payload,
};
default:
return state;
}
};
import { countReducer, updateNavigation } from "../reducers/countReducer";
import { combineReducers } from "redux";
const allReducers = combineReducers({
countReducer,
updateNavigation,
});
const store = createStore(allReducers);
console.log(store.getState());
Output:
{ countReducer: { count: 0 }, updateNavigation: { navigation: {} } }
In your action/index.js
import { COUNTER_CHANGE, UPDATE_NAVIGATION } from "../constants";
export function changeCount(count) {
dispatch( {
type: COUNTER_CHANGE,
payload: count,
});
}
export function updateNavigation(obj) {
dispatch({
type: UPDATE_NAVIGATION,
payload: obj,
});
}
Dispatch the data without returning it
I am working on a proof of concept in react-native and I am using redux-persist. According to what I read the states are automatically assigned in version 5, however I have not managed to rehydrate the state without the case REHYDRATE.
My reducer:
import {ADD_USER} from '../constants/actionTypes';
import { REHYDRATE } from 'redux-persist';
const initialState = {
userName: null,
fetching: false
};
const userReducer = (state = initialState, action) => {
let copied = Object.assign({}, state);
switch (action.type){
case ADD_USER:
copied.userName = action.payload;
break;
case REHYDRATE:
//console.log('Payload desde el reducer ', action.payload);fsdffdsdsfsdfsdf
copied = action.payload.userReducer;
break;
default:
break;
}
return copied;
};
export default userReducer;
My configureStore
const persistConfig = {
key: 'root',
storage,
stateReconciler: autoMergeLevel2,
};
function logger({ getState }) {
return (next) => (action) => {
let returnValue = next(action)
return returnValue
}
}
const persistedReducer = persistCombineReducers(persistConfig, {userReducer});
export default () => {
let store = createStore(persistedReducer, undefined, applyMiddleware(logger));
let persistor = persistStore(store, null, () => {
Alert.alert(
'Current state',
JSON.stringify(store.getState()),
[
{text: 'OK', onPress: () => console.log('OK Pressed')},
],
{ cancelable: false }
);
});
return { store, persistor }
}
My problem was that I returned the state at the end of the method instead of returning it in each case of the switch and this surely broke the auto rehydration. Changing the reducer returns my problem disappeared.
The reducer code was like this:
const initialState = {
userName: null,
fetching: false
};
const userReducer = (state = initialState, action) => {
switch (action.type){
case ADD_USER:{
let copied = Object.assign({}, state);
copied.userName = action.payload;
return copied;
}
default:
return state;
}
};
export default userReducer;
i have a code to update the count value of object. now i need to convert it to immutable.js. i am pretty much new to immutable.js. so how can i convert this code to immutable.js format.
import * as actionTypes from '../actions/actionTypes';
const initialState={
ingredients: {
salad:0,
meat:0,
cheese:0,
},
totalprice:40
}
const ingredientCost = {
cheese:20,
meat:30,
salad:10,
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.ADD_INGREDIENT:
return {
...state,
ingredients:{
...state.ingredients,
[action.ingredientName] : state.ingredients[action.ingredientName]+1
},
totalprice: state.totalprice + ingredientCost[action.ingredientName],
};
case actionTypes.REMOVE_INGREDIENT:
return {
...state,
ingredients:{
...state.ingredients,
[action.ingredientName] : state.ingredients[action.ingredientName]-1,
},
totalprice: state.totalprice - ingredientCost[action.ingredientName],
};
default:
return state;
}
};
export default reducer;
i tried to change the code to immutable format:
import * as actionTypes from '../actions/actionTypes';
import Immutable from 'immutable';
const initialState=Immutable.fromJS({
ingredients: {
salad:0,
meat:0,
cheese:0,
},
totalprice:40
})
const ingredientCost = {
cheese:20,
meat:30,
salad:10,
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.ADD_INGREDIENT:
return state.ingredients[action.ingredientName].merge(state.ingredients[action.ingredientName]+1)
case actionTypes.REMOVE_INGREDIENT:
return {
...state,
ingredients:{
...state.ingredients,
[action.ingredientName] : state.ingredients[action.ingredientName]-1,
},
totalprice: state.totalprice - ingredientCost[action.ingredientName],
};
default:
return state;
}
};
export default reducer;
but while doing this i am not able to update the state. i am getting initialstate but i dont know how to update it.
thanks in advance
since initialState is immutable object, You cant use state.ingredients, You should use
state.set() / state.setIn() or
state.update() / state.updateIn() or
state.merge() / state.mergeIn()
read: https://facebook.github.io/immutable-js/docs/#/Map
In my react/redux application have the following actions
export function getSeatingChartConfiguration(team) {
return function(dispatch) {
ref.child(team.key).once('value').then(function(snapshot) {
dispatch(loadSeatingChart(snapshot.val()));
});
};
}
export function saveSeatingChartSection(key, sectionData){
return function(dispatch) {
ref.child(key).once('value').then(function(snapshot) {
let data = snapshot.val();
let sections = data.sections;
let index = snapshot.val().sections.map( (el) => el.name).indexOf(sectionData.name);
if(index !== -1) {
sections[index] = sectionData;
} else {
sections.push(sectionData);
}
data.sections = sections;
ref.child(key).update(data, function(error) {
dispatch(loadSeatingChart(data));
});
});
};
}
Here is the reducer
export default function seatingChart(state = {}, action) {
switch(action.type) {
case actionTypes.LOAD_SEATING_CHART_CONFIGURATION:
return action.seatingChartConfiguration;
default:
return state;
}
}
I am not getting any errors when getSeatingChartConfiguration() is called but I have receiving Error: A state mutation was detected between dispatches, in the pathseatingChart.sections.2.points.What do I need to change in my action or reducer to not mutate the state.
Use Object.assign with initial state to avoid state mutation like below,
const initialState = { seatingChartConfiguration: [] }
export default function seatingChart(state = initialState, action) {
switch(action.type) {
case actionTypes.LOAD_SEATING_CHART_CONFIGURATION:
return Object.assign({}, state, { seatingChartConfiguration: action.seatingChartConfiguration });
default:
return state;
}
}