Ata Mohammadi
Ata Mohammadi

Reputation: 3530

React Native: Redux-persist, Result of actions not being persisted

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

Answers (1)

Jaseem Abbas
Jaseem Abbas

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

Related Questions