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

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

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.

Related

How to use reducer in redux in react

I am using Redux for state management, I have faced an issue in reducer function
here is the image of my console, You can see the Product Action is providing my data but the reducer is not passing on my function
here is my code of ProductAction:
export const getProductsbyFind = (myvariable) =>async (dispatch)=>{
try {
console.log(myvariable)
dispatch({type: ALL_PRODUCTS_REQUEST_BY_ID})
const{ data } = await axios.get(`/api/v1/product/${myvariable}`)
console.log(data)
dispatch({
type: ALL_PRODUCTS_SUCCESS_BY_ID,
payload: data
})
} catch (error) {
dispatch({
type:ALL_PRODUCTS_FAIL,
payload: error.response.data.message
})
}
}
here is the code of Reducer:
export const productReducersById = (state = { products: [] }, action) => {
switch (action.type) {
case ALL_PRODUCTS_REQUEST_BY_ID:
return {
loading: true,
products: []
}
case ALL_PRODUCTS_SUCCESS_BY_ID:
return {
loading: false,
products: action.payload.products,
productsCount: action.payload.productsCount
}
case UPDATE_QUANTITY_BY_ID:
const { index, quantity } = action.payload;
const prods = state.products.map((p, i) => {
if (i !== index)
return p;
return {
...p,
quantity
}
});
return {
loading: true,
products: prods
}
case ALL_PRODUCTS_FAIL_BY_ID:
return {
loading: false,
error: action.payload
}
case CLEAR_ERRORS_BY_ID:
return {
...state,
error: null
}
default:
return state
}
}
here is the code of my page where I want to get my data:
const { loading, products, error, productCount } = useSelector(state => state.products);
console.log(products)
useEffect(() => {
dispatch(getProductsbyFind(myvariable));
}, [dispatch])
You have a typo in your reducer:
case ALL_PRODUCTS_SUCCESS_BY_ID:
return {
loading: false,
- products: action.payload.products,
+ products: action.payload.product,
productsCount: action.payload.productsCount
}
(Also, productsCount does not exist in your payload, so that will become undefined.)

Async api fetch with redux thunk

I'm having trouble fetching a list of users from an api. I think issue might be in my mapDispatchToProps function but I'm not sure. Everything else seems fine to me. I'm new to redux and I'm kinda having a hard time wrapping my head around it so any help is appreciated
The list with the users would ideally be displayed as soon as the component mounts. I did the same thing without redux store and it was working just fine, I'm just not really sure how to integrate redux
Actions
export const startLoading = () => {
return {
type: START_LOADING
}
}
export const updateUserData = payload => {
return {
type: UPDATE_USER_DATA,
payload
}
}
export const updateUserError = payload => {
return {
type: UPDATE_USER_ERROR,
payload: payload
}
}
export function fetchUsers() {
return dispatch => {
dispatch(startLoading());
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => {
data = data.filter(user => user.id < 4);
data.forEach(user => {
user.isGoldClient = false;
user.salary = '4000';
user.photo = userThumbnail;
})
.then(data => {
dispatch(updateUserData(data));
}).catch(error => {
dispatch(updateUserError(error));
})
});
};
};
Reducers
const initialState = {
loading: false,
users: [],
error: null
};
export function userReducer(state=initialState, action){
switch(action.type){
case START_LOADING:
return {
...state,
loading: true
}
case UPDATE_USER_DATA:
return {
...state,
loading: false,
users: action.payload,
error: null
}
case UPDATE_USER_ERROR:
return {
...state,
error: action.payload,
loading: false,
users: []
};
default:
return state;
};
};
Component
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [],
usersAreDisplayed: true
};
}
componentDidMount() {
fetchUsers();
}
render(){
return (
<UserList users={this.state.users} />
)
}
}
function mapStateToProps(state){
return { users: state.users }
}
function mapDispatchToProps(dispatch){
return {
fetchUsers: payload => dispatch(updateUserData(payload)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Looks like you are not calling the actual fetchUsers at all.
Change the component code like this
function mapStateToProps(state){
return { users: state.users }
}
// remove this function
// function mapDispatchToProps(dispatch){
// return {
// fetchUsers: payload => dispatch(updateUserData(payload)),
// }
// }
export default connect(mapStateToProps, {fetchUsers})(Home); //<---- destructure it here. Also import the function (action)
1a. fetchUsers function needs to be accessed using this.props
componentDidMount() {
this.props.fetchUsers();
}
There is an extra then block after forEach.
Remove it.
export function fetchUsers() {
return (dispatch) => {
dispatch(startLoading());
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => {
data = data.filter((user) => user.id < 4);
data.forEach((user) => {
user.isGoldClient = false;
user.salary = "4000";
user.photo = userThumbnail;
});
dispatch(updateUserData(data)); // <------ no extra .then is required
})
.catch((error) => {
dispatch(updateUserError(error));
});
};
}
Also <UserList users={this.state.users} /> needs to be <UserList users={this.props.users} /> As already mentioned by #Nsevens
You are mapping redux state into your component's props.
So you should load the users from the component's props and not it's state:
render(){
return (
<UserList users={this.props.users} />
)
}

React app 'Slowing down' when i try to dispatch action inside firebase.auth().onAuthStateChanged() method

I am trying to implement Firebase Authentication in my React App.
I have buttons LogIn and LogOut, which are working correctly(i can console.log uid)
Redirecting is fine as well.
Then i tried to add new reducer 'user' : {uid: uid} or 'user': {} if logged out.
At this point my App doesn't want to run and browser shows the notification "A web page is slowing your browser".
Thats GitHub link
App.js:
import ReactDOM from "react-dom";
import Navigation from "../routers/Navigation";
import { firebase, database } from "../firebase/firebase";
import { startSetExpenses, setFiltersText } from "../redux/actions";
import { createBrowserHistory } from "history";
import { connect } from "react-redux";
import {logIn, logOut} from '../redux/actions'
let history = createBrowserHistory();
export function App(props) {
firebase.auth().onAuthStateChanged((user) => {
if (!user) {
props.dispatch(logOut())
history.push("/");
} else {
props.dispatch(logIn(user.uid))
if (history.location.pathname === "/") {
history.push("/dashboard");
}
}
});
return (
<div>
<h1>Expenses App</h1>
<Navigation history={history}/>
</div>
);
}
let mapStoreToProps = (dispatch) => {
return {
dispatch
}
}
export default connect(mapStoreToProps)(App)
RootReducer.js :
const initialState = {
filters: {
text: ""
},
expenses: [],
user: {}
};
function userReducer(state = {}, action) {
const {type, uid} = action
switch (type) {
case "LOGIN":
return {
uid: uid
}
case "LOGOUT":
return {}
default :
return state;
}
}
function expensesReducer(state = initialState.expenses, action) {
const { type,id, description, value, updates,expenses } = action;
switch (type) {
case "ADD_EXPENSE":
return [
...state,
{
id,
description,
value,
},
];
case "REMOVE_EXPENSE":
if (state.findIndex(expense => expense.id === id) < 0) throw new Error('index is not found')
return state.filter((expense) => expense.id !== id);
case "UPDATE_EXPENSE":
return state.map((expense) => {
return expense.id === id ? { ...expense, ...updates } : expense;
});
case "SET_EXPENSES":
if (expenses) {
return expenses
}
default:
return state;
}
}
function filtersReducer(state = initialState.filters, action) {
const {type, text} = action;
switch (type) {
case 'SET_FILTERS_TEXT':
return {...state, text}
default:
return state;
}
}
const rootReducer = combineReducers({
filters: filtersReducer,
expenses: expensesReducer,
user: userReducer
})
export default rootReducer
Actions.js:
import {database, firebase, googleProvider} from "../firebase/firebase";
export function logIn(uid) {
return (dispatch) => {
console.log('uid inside actionCreator', uid)
dispatch({
type: "LOGIN",
uid: uid
})
}
}
export function logOut() {
return (dispatch) => {
console.log('logged out')
dispatch({type: 'LOGOUT'})
}
}
export function startLogIn() {
return () => {
return firebase.auth().signInWithPopup(googleProvider)
}
}
export function startLogOut() {
return () => {
return firebase.auth().signOut()
}
}
export function addExpense({ id, description, value } = {}) {
return {
type: "ADD_EXPENSE",
id,
description,
value,
};
}
export const startAddExpense = (expense) => {
return (dispatch) => {
let newId = database.ref("expenses").push().key;
const { description, value } = expense;
return database
.ref("expenses/" + newId)
.set({ description, value })
.then(() => {
dispatch(
addExpense({
type: "ADD_EXPENSE",
id: newId,
description,
value,
})
);
});
};
};
export function removeExpense(id) {
return {
type: "REMOVE_EXPENSE",
id,
};
}
export const startRemoveExpense = (id) => {
return (dispatch) => {
return database
.ref("expenses/" + id)
.remove()
.then(() => {
console.log("removing expense with id : " + id);
dispatch(removeExpense(id));
})
.catch((error) => {
console.log(`Expense with id:${id} was not removed`);
console.log(error)
});
};
};
export function updateExpense(id, updates) {
return {
type: "UPDATE_EXPENSE",
id,
updates,
};
}
export const startUpdateExpense = (id, updates) => {
return (dispatch) => {
return database.ref('expenses/' + id)
.update(updates)
.then(() => {
dispatch(updateExpense(id, updates))
})
.catch((err) => {
console.log('Error with updating an expense from firebase')
console.log(err)
})
}
}
export function setFiltersText(text) {
return {
type: "SET_FILTERS_TEXT",
text,
};
}
export const setExpenses = (expenses) => {
return {
type: "SET_EXPENSES",
expenses: [...expenses],
};
};
export const startSetExpenses = () => {
return (dispatch) => {
//get expenses from database
//.then dispatch expenses to state with setExpenses
return database
.ref("expenses")
.once("value")
.then((snapshot) => {
const expensesObj = snapshot.val();
let expenses = [];
for (let property in expensesObj) {
expenses = [
...expenses,
{
id: property,
description: expensesObj[property].description,
value: expensesObj[property].value,
},
];
}
dispatch(setExpenses(expenses));
});
};
};

Redux proper send of payload

I'm new to Redux. And I'm trying to create a simple FETCH_ALL_POSTS.
actions
export const fetchPosts = () => async dispatch => {
const response = await jsonPlaceholder.get('/posts');
console.log(response.data)
dispatch({
type: FETCH_ALL_POSTS,
payload: response.data
})
}
posts reducer
export default (state = {}, action) => {
const { type, payload } = action;
switch (type) {
case FETCH_ALL_POSTS:
return {
...state, payload
}
default:
return state
}
}
post list component
const mapStateToProps = state => {
console.log(Object.values(state.posts))
return {
posts: state.posts
}
}
This is working but the data that I'm getting from mapStateToProps is not what I'm expecting.
Result : "array: [ 0:[{},{},{}] ]"
My expected result: "array:[{},{},{}]"
Try this,
const initialState = {
posts: '',
}
export default (state=initialState, action) => {
const { type, payload } = action;
switch (type) {
case FETCH_ALL_POSTS:
return{
posts:state.posts=action.payload.posts
}
default:
return state
}
}

React Native Firebase Fetching Only One Data

How can I fetch only one data and write it to Header ?
I am using firebase and react-redux.
firebase structure i try to write "organization": inovanka:
Action File Codes:
import firebase from 'firebase';
import { Actions } from 'react-native-router-flux';
import { ORGANIZATION_NAME_DATA_SUCCESS } from './types';
export const organizationName = () => {
const { currentUser } = firebase.auth();
return (dispatch) => {
firebase.database().ref(`/organizations/${currentUser.uid}`)
.on('value', snapshot => {
dispatch({ type: ORGANIZATION_NAME_DATA_SUCCESS, payload: snapshot.val() });
});
};
}
Reducer File :
import { ORGANIZATION_NAME_DATA_SUCCESS } from '../actions/types';
const INITIAL_STATE = {
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case ORGANIZATION_NAME_DATA_SUCCESS:
console.log(action); // data retrieved as array
return action.payload
default:
return state;
}
};
Component: (I would like to write it to this)
class HomePage extends Component {
componentWillMount() {
}
render() {
return (
<Container>
<Header>
<Text> i would like to write it here </Text>
</Header>
<Content>
</Content>
</Container>
);
}
}
const mapStateToProps = ({ homepageResponse }) => {
const organizationArray = _.map(homepageResponse, (val, uid) => {
return { ...val, uid }; //
});
return { organizationArray };
};
export default connect(mapStateToProps, { organizationName })(HomePage);
Change this:
firebase.database().ref(`/organizations/${currentUser.uid}`)
.on('value', snapshot => {
to this:
firebase.database().ref(`/organizations/${currentUser.uid}`)
.once('value', snapshot => {
using once() will read data only one time, thus fetching only one data
Solution is Here !
Action File:
export const organizationName = () => {
const { currentUser } = firebase.auth();
return (dispatch) => {
firebase.database().ref(`/organizations/${currentUser.uid}`)
.once('value', snapshot => {
_.mapValues(snapshot.val(), o => {
console.log(o);
dispatch({ type: ORGANIZATION_NAME_DATA_SUCCESS, payload: {organization: o.organization, fullname: o.fullname }});
});
});
};
}
Reducer File
const INITIAL_STATE = {
organization: '',
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case ORGANIZATION_NAME_DATA_SUCCESS:
console.log(action);
return {...state, organization:action.payload.organization };
default:
return state;
}
};
Component File MapToStateProps and componentWillMount
const mapStateToProps = state => {
const { organization, fullname } = state.homepageResponse;
console.log("burada" + organization);
return { organization, fullname };
};
componentWillMount(){
this.props.organizationName();
}
*Last Step Header *
render() {
return (
<Container>
<Header>
<Text> { this.props.organization } </Text>
</Header>
</Container>
}
Thank You Everyone

Categories