Reputation: 3530
I've a RN app with react-redux, redux-thunk and redux-persist.
everything was fine, I've changed the reducers and added new actions. But unfortunately state is not being persisted any more. Whatever action I trigger, after reloading the app, i see it's not persisted.
What could cause it?
here is my actions/reducers :
import { REHYDRATE } from 'redux-persist/constants';
import {Actions as routes} from 'react-native-router-flux';
export const types = {
GET_COURSES: 'GET_COURSES',
AUTHENTICATE: 'AUTHENTICATE',
AUTHENTICATE_FAILED: 'AUTHENTICATE_FAILED',
GET_EVALUATION: 'GET_EVALUATION',
GET_EVALUATION_FAILED: 'GET_EVALUATION_FAILED',
SUBMIT_EVALUATION: 'SUBMIT_EVALUATION',
SUBMIT_EVALUATION_FAILED: 'SUBMIT_EVALUATION_FAILED',
BOOK_COURSE: 'BOOK_COURSE',
UNBOOK_COURSE: 'UNBOOK_COURSE',
BOOK_COURSE_FAILED: 'BOOK_COURSE_FAILED',
UNBOOK_COURSE_FAILED: 'UNBOOK_COURSE_FAILED'
};
import Functions from './common/Functions';
export const actionCreators = {
getEvaluations: (users) => {
return dispatch => {
Functions.getEvaluationsAPI(users)
.then((data)=>{
const {evaluationsList} = data;
return dispatch(evaluationSuccess(evaluationsList));
})
.catch((e)=>{
return dispatch(evaluationFailed(e));
});
}
},
getCourses: (users) => {
console.log('getting courses, user', users);
return dispatch => {
Functions.getCoursesAPI(users)
.then((data)=>{
const {courseList, lectureList, schedule, eventList, discussion, coursesArray} = data;
return dispatch(coursesSuccess(courseList, lectureList, schedule, eventList, discussion, coursesArray));
})
.catch((e)=>{
return dispatch(coursesFailed(e));
});
}
},
bookCourse: (user, courseId, callback) => {
return dispatch => {
Functions.bookCourse(user, courseId, callback)
.then(()=>{
Functions.getCoursesAPI([user])
.then((data)=>{
const {courseList, lectureList, schedule, eventList, discussion, coursesArray} = data;
return dispatch(coursesSuccess(courseList, lectureList, schedule, eventList, discussion, coursesArray));
})
.catch((e)=>{
return dispatch(coursesFailed(e));
});
})
.catch((e)=>{
return dispatch(coursesFailed(e));
})
}
},
unbookCourse: (user, courseId, callback) => {
return dispatch => {
Functions.unbookCourse(user, courseId, callback)
.then(()=>{
Functions.getCoursesAPI([user])
.then((data)=>{
const {courseList, lectureList, schedule, eventList, discussion, coursesArray} = data;
return dispatch(coursesSuccess(courseList, lectureList, schedule, eventList, discussion, coursesArray));
})
.catch((e)=>{
return dispatch(coursesFailed(e));
});
})
.catch((e)=>{
return dispatch(coursesFailed(e));
})
}
},
submitEvaluation: (user, users, evaluationId, evaluationData, callback) => {
return dispatch => {
Functions.submitEvaluation(user, users, evaluationId, evaluationData, callback)
.then(()=>{
Functions.getEvaluationsAPI(users)
.then((data)=>{
const {evaluationsList} = data;
return dispatch(evaluationSuccess(evaluationsList));
})
.catch((e)=>{
return dispatch(evaluationFailed(e));
});
})
.catch((e)=>{
return dispatch(evaluationSubmissionFailed(e));
})
}
},
authenticate: (logincode, callback) => {
return dispatch => {
Functions.login(logincode)
.then((response)=>{
console.log('authenticate results:', response);
return dispatch(loginSuccess(response));
})
.then(()=>{
routes.tabbar();
})
.catch((e)=>{
console.log('authenticate error:', e);
callback(e.message);
return dispatch(loginFailed(e.message));
})
}
}
}
const loginSuccess = (response) => {
return {
type: types.AUTHENTICATE,
payload: response
};
};
const loginFailed = (response) => {
return {
type: types.AUTHENTICATE_FAILED,
payload: {
error: response
}
};
};
const evaluationSuccess = (evaluationsList) => {
return {
type: types.GET_EVALUATION,
payload: {
evaluations: evaluationsList
}
};
};
const evaluationFailed = (e) => {
return {
type: types.GET_EVALUATION_FAILED,
payload: {
error: e
}
};
};
const evaluationSubmissionSuccess = (evaluationsList) => {
return {
type: types.SUBMIT_EVALUATION,
payload: {
evaluatiosn: evaluationsList
}
};
};
const evaluationSubmissionFailed = (e) => {
return {
type: types.SUBMIT_EVALUATION_FAILED,
payload: {
error: e
}
};
};
const coursesSuccess = (courses, lectures, schedule, eventList, discussion, coursesArray) => {
return {
type: types.GET_COURSES,
payload: {
courses: courses,
lectures: lectures,
schedule: schedule,
events: eventList,
discussion: discussion,
coursesArray: coursesArray
}
};
};
const coursesFailed = (e) => {
return {
type: types.GET_COURSES_FAILED,
payload: {
error: e
}
};
};
const initialState = {
rehydrated: false,
user: null,
login: false,
users: {},
courses: {},
schedules: {},
evaluations: {},
lectures: {},
goals: {},
discussions: {},
attendance: {},
events: {}
}
export const reducer = (state = initialState, action) => {
const {user, users, courses, login, schedules, evaluations, goals, discussions, attendance, events, lectures} = state;
const {type, payload} = action;
switch (type) {
case types.GET_COURSES: {
return {
...state,
courses: payload.coursesArray,
lectures: payload.lectures,
schedules: payload.schedule,
events: payload.events,
discussions: payload.discussion
}
}
case types.GET_COURSES_FAILED: {
return {
...state
}
}
case types.AUTHENTICATE: {
let newObj = users;
newObj[payload.userId] = payload;
let newCourses = courses;
newCourses[payload.userId] = [];
let newschedules = schedules;
newschedules[payload.userId] = [];
let newevaluations = evaluations;
newevaluations[payload.userId] = [];
let newgoals = goals;
newgoals[payload.userId] = [];
let newdiscussions = discussions;
newdiscussions[payload.userId] = [];
let newattendance = attendance;
newattendance[payload.userId] = [];
let neweventList = events;
neweventList[payload.userId] = [];
let newlectures = lectures;
newlectures[payload.userId] = [];
return {
...state,
login: true,
user: payload.userId,
users: newObj,
courses: newCourses,
schedules: newschedules,
evaluations: newevaluations,
goals: newgoals,
discussions: newdiscussions,
attendance: newattendance,
events: neweventList,
lectures: newlectures
}
}
case types.AUTHENTICATE_FAILED: {
return {
...state
}
}
case types.GET_EVALUATION: {
return {
...state,
evaluations: payload.evaluations
}
}
case types.GET_EVALUATION_FAILED: {
return {
...state
}
}
case types.SUBMIT_EVALUATION: {
return {
...state,
evaluations: payload.evaluations
}
}
case types.SUBMIT_EVALUATION_FAILED: {
return {
...state
}
}
case types.BOOK_COURSE: {
return {
...state
}
}
case types.BOOK_COURSE_FAILED: {
return {
...state
}
}
case types.UNBOOK_COURSE: {
return {
...state
}
}
case types.UNBOOK_COURSE_FAILED: {
return {
...state
}
}
case REHYDRATE: {
return {
...state,
rehydrated: true
}
}
}
return state
}
****UPDATE : store configuration :
import React from "react";
import { View, AsyncStorage } from 'react-native'
import { applyMiddleware, createStore, compose } from 'redux'
import { Provider } from 'react-redux'
import { persistStore, autoRehydrate } from 'redux-persist'
import thunk from 'redux-thunk'
import createLogger from 'redux-logger'
import { reducer } from './reducers'
import Routes from './Routes'
const logger = createLogger();
const store = createStore(reducer, compose(
applyMiddleware(
thunk,
logger
)
), autoRehydrate({ log: true }))
persistStore(store, {storage: AsyncStorage})
const Root = () => (
<Provider store={store}>
<Routes />
</Provider>
)
export default Root
Upvotes: 2
Views: 1772
Reputation: 5230
Sometimes, you are trying to dispatch an action, but your view does not update. Why does this happen? There may be several reasons for this.
Never mutate reducer arguments
Every single time, you must return the new state object. Even if you don't use a library like Immutable, you need to completely avoid mutation.
For example, a reducer like this is wrong because it mutates the state:
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
// Wrong! This mutates state
state.push({
text: action.text,
completed: false
})
return state
case 'COMPLETE_TODO':
// Wrong! This mutates state[action.index].
state[action.index].completed = true
return state
default:
return state
}
}
It needs to be rewritten like this:
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
// Return a new array
return [
...state,
{
text: action.text,
completed: false
}
]
case 'COMPLETE_TODO':
// Return a new array
return state.map((todo, index) => {
if (index === action.index) {
// Copy the object before mutating
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
default:
return state
}
}
It's more code, but it's exactly what makes Redux predictable and efficient.
Don't forget to call dispatch(action)
Don't forget to call dispatch(action)
Make sure mapStateToProps is correct
It's possible you're correctly dispatching an action and applying your reducer but the corresponding state is not being correctly translated into props.
Here's a complete troubleshooting article from redux for the same problem.
Upvotes: 3