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;
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'm having a challenge with redux as I keep getting this error: TypeError: undefined is not an object (evaluating '_useSelector.attendance'). Everything seems to be working fine but I just done understand why it keeps coming back to this even when it seems the code is okay to my knowledge
Reducers.js
import { GET_ATTENDANCE, ADD_TO_ATTENDANCE_LIST } from "./actions";
const initialState = () => ({
attendance: [],
attendancebook: [],
});
function attendanceReducer(state = initialState, action) {
switch (action.type) {
case GET_ATTENDANCE:
return { ...state, attendance: action.payload };
case ADD_TO_ATTENDANCE_LIST:
return {
...state,
attendancebook: [...state.attendancebook, action.payload],
};
default:
return state;
}
}
export default attendanceReducer;
AttendanceScreen.js
function AttendanceScreen({ route }) {
const navigation = useNavigation();
const listing = route.params;
const dispatch = useDispatch();
const { attendance, attendancebook } = useSelector(
(state) => state.attendanceReducer
);
const getAttendance = () => {
try {
dispatch({
type: GET_ATTENDANCE,
payload: attendancelist,
});
} catch (error) {
console.log(error);
}
};
const fetchAttendance = () => dispatch(getAttendance());
const addToAttendanceList = (data) => dispatch(addAttendance(data));
useEffect(() => {
fetchAttendance();
}, []);
store.js
import attendanceReducer from "./reducers";
const persistConfig = {
key: "root",
storage: AsyncStorage,
whitelist: ["attendancebook"],
};
const rootReducer = combineReducers({
attendanceReducer: persistReducer(persistConfig, attendanceReducer),
});
export const store = createStore(rootReducer, applyMiddleware(thunk));
export const persistor = persistStore(store);
actions.js
export const GET_ATTENDANCE = "GET_ATTENDANCE";
export const ADD_TO_ATTENDANCE_LIST = "ADD_TO_ATTENDANCE_LIST";
export const addAttendance = (data) => (dispatch) => {
dispatch({
type: ADD_TO_ATTENDANCE_LIST,
payload: data,
});
};
Please any help will be appreciated.
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
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;
}
};
Dispatch multiple actions in redux-thunk and receive infinite loop.
I am trying to turn on a spinner before a request goes to the back-end and stop spinner after request succeeds or fails.
Does anyone have any idea about where I've done mistake?
My code looks like this.
logic.js
import * as actions from "./actions";
export const getData = () => {
return dispatch => {
dispatch(actions.startSpinner());
setAuthorizationToken(getToken());
axiosInstance
.get("/data")
.then(response => {
dispatch(actions.stopSpinner()); //I guess this is problem ?
dispatch(actions.getData(response.data));
})
.catch(err => {
console.log(err);
dispatch(actions.stopSpinner());
});
};
};
And file actions.js
export const startSpinner = () => {
return { type: actionTypes.START_SPINNER };
};
export const stopSpinner = () => {
return { type: actionTypes.STOP_SPINNER };
};
export const getData = data => {
return {
type: actionTypes.GET_DATA,
payload: data
};
};
And reducer for it spinner.js
import actionTypes from "../actionTypes";
export default (state = false, action) => {
switch (action.type) {
case actionTypes.START_SPINNER: {
return (state = true);
}
case actionTypes.STOP_SPINNER: {
return (state = false);
}
default: {
return state;
}
}
};
And reducer for data dataReducer.js
import actionTypes from "../actionTypes";
const defaultState = [];
export default (state = defaultState, action) => {
switch (action.type) {
case actionTypes.GET_DATA: {
let newState = [...action.payload];
return newState;
}
default: {
return state;
}
}
};