I have this code for my context provider, I have my wrapped in component but still when I try to use it in a child using either useProductState or useProductDispatch, it returns undefined (throws err);
import React from "react";
import productsReducer from "./productsReducer";
const ProductsStateContext = React.createContext();
const ProductsDispatchContext = React.createContext();
const initialState = {
restaurantTitle: "",
restaurantId: "VljSa5Eakepw9QkTAUOW",
productsCollection: "",
categories: [],
defaultCategory: "",
isLoading: true,
};
function ProductsProvider({ children }) {
const [state, dispatch] = React.useReducer(productsReducer, initialState);
return (
<ProductsStateContext.Provider value={state}>
<ProductsDispatchContext.Provider value={dispatch}>
{children}
</ProductsDispatchContext.Provider>
</ProductsStateContext.Provider>
);
}
function useProductsState() {
const context = React.useContext(ProductsStateContext);
if (context === undefined) {
throw new Error("useProductsState must be used within a ProductsProvider");
}
return context;
}
function useProductsDispatch() {
const context = React.useContext(ProductsDispatchContext);
if (context === undefined) {
throw new Error(
"useProductsDispatch must be used within a ProductsProvider"
);
}
return context;
}
export { ProductsProvider, useProductsState, useProductsDispatch };
Can somebody explain how this works, I'm trying to access state and dispatch into a functional component that is a child of .
UPDATE:
I've got this as an action for my reducer
case "FETCH_RESTAURANT_DATA": {
return fetchRestaurantData(state, action.payload);
}
Function body looks like this:
const fetchRestaurantData = (state, value) => {
let newState = state;
return axios
.post(api.routes.restaurant, { restaurantId: state.restaurantId })
.then((res) => {
newState.restaurantTitle = res.data.restaurantTitle;
res.data.categories.forEach(
(category) =>
(newState.categories[category] = {
loaded: false,
props: [],
})
);
newState.defaultCategory = res.data.categories[0];
newState.productsCollection = res.data.productsCollection;
newState.isLoading = false;
return axios.post(api.routes.category, {
productsCollection: res.data.productsCollection,
categoryId: newState.defaultCategory,
});
})
.then((res) => {
newState.categories[newState.defaultCategory].props =
res.data[newState.defaultCategory];
newState.categories[newState.defaultCategory].loaded = true;
console.log(newState);
return newState;
});
};
What i think is going on, I think in reducer it does not wait for my response and update context state with an undefined value which then triggers my error.
I have tried to make a middle async function that awaits for fetchRestaurantData() response but it is still updating before getting a response
You should wait for the response in fetchRestaurantData:
const fetchRestaurantData = async (state, value) => { // add async keyword to the function
let newState = state;
return await axios // here add await
.post(api.routes.restaurant, { restaurantId: state.restaurantId })
.then((res) => {
newState.restaurantTitle = res.data.restaurantTitle;
res.data.categories.forEach(
(category) =>
(newState.categories[category] = {
loaded: false,
props: [],
})
);
newState.defaultCategory = res.data.categories[0];
newState.productsCollection = res.data.productsCollection;
newState.isLoading = false;
return axios.post(api.routes.category, {
productsCollection: res.data.productsCollection,
categoryId: newState.defaultCategory,
});
})
.then((res) => {
newState.categories[newState.defaultCategory].props =
res.data[newState.defaultCategory];
newState.categories[newState.defaultCategory].loaded = true;
console.log(newState);
return newState;
});
};
More information about the async functions
Related
The component does not re render after successfully update state in redux
i have tried to do some condition in componentShouldUpdate end up with loading true without change
reducer.js
import * as types from "./actionsType";
const INITIAL_STATE = {
slide_data: [],
error: null,
loading: false,
};
const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties,
};
};
const slideReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.SLIDES_FETCH_START:
return updateObject(state, {
error: null,
loading: true,
});
case types.SLIDES_FETCH_SUCSSES:
return updateObject(state, {
slide_data: action.payload,
error: null,
loading: false,
});
case types.SLIDES_FETCH_FAIL:
return updateObject(state, {
error: action.error,
loading: false,
});
default:
return state;
}
};
export default slideReducer;
actions.js
import * as types from "./actionsType";
import axios from "axios";
import { selectSlides } from "./slides.selectors";
export const slidesStart = () => {
return {
type: types.SLIDES_FETCH_START,
};
};
export const slidesSucces = (slides) => {
return {
type: types.SLIDES_FETCH_SUCSSES,
payload: slides,
};
};
export const slidesFail = (error) => {
return {
type: types.SLIDES_FETCH_FAIL,
error: error,
};
};
export const fetchSlides = () => {
return (dispatch) => {
console.log("fetch Start");
dispatch(slidesStart());
axios
.get("http://127.0.0.1:8000/slides/", {
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
dispatch(slidesSucces(res.data));
})
.catch((err) => dispatch(slidesFail(err)));
};
};
component
class IntroPage extends Component {
constructor(props) {
super(props);
this.tlitRef = React.createRef();
this.titlelRef = React.createRef();
this.subTitleRef = React.createRef();
this.showcase = React.createRef();
}
componentDidMount() {
this.props.fetchSlides();
}
render() {
const { slides, loading } = this.props;
if (loading) {
return <h1>Loading</h1>;
}
return (
<div className="intro">
<div className="wrapper">
{slides.map((data) => (
<SwiperSlides data={data} key={data.name} />
))}
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.slides.loading,
error: state.slides.error,
slides: state.slides.slide_data,
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchSlides: () => dispatch(fetchSlides()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(IntroPage);
Register the redux-logger correctly. The data was returned but nothing changes when I do redux-persist and try to reload data come through
update ::
when I change the size of the browser data it correctly appears what is this !!
update : this problem related to swiperjs
and the solution will be like that:
1 - assign swiper instance to React.CreateRef(null) : this.swiper = React.CreateRef(null)
2 - in componentDidUpdate() make a swiper update : this.swiper.current.update()
4 - use a arrow function syntax in swiper on functions to refer to the outer scope
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} />
)
}
I have a post details component where on clicking the like button the redux state changes the redux state is like
posts
->postDetails
I'am changing the liked property and number of likes of postDetais object, On clicking the like button the liked property is set to true from false and vice versa and the number of likes is incremented.
However the state is changing but the componentDidUpdate method is not firing
PostDetails.js
import React, { Component } from "react";
import { connect } from "react-redux";
import {
getPostData,
likePost,
unlikePost
} from "../../store/actions/postsActions";
import { Icon, Tooltip } from "antd";
import { Link } from "react-router-dom";
export class PostDetails extends Component {
state = {
postData: this.props.postDetails
};
componentDidMount() {
this.props.getPostData(this.props.match.params.post_id);
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log(this.props.postDetails);
if (prevProps.postDetails !== this.props.postDetails) {
this.setState({
postData: this.props.postDetails
});
}
}
render() {
const { postData } = this.state;
const liked = postData.liked;
return (
<div className="postDetails">
{postData && (
<div className="postDetailsContainer">
<div className="postImage">
<img src={postData.imageUrl} alt={postData.caption} />
</div>
<div className="postContent">
<div className="postContent__header">
<Link
to={`/user/${postData.username}`}
className="postContent__headerContent"
>
<img
src={postData.profileUrl}
alt={postData.username}
className="postContent__profileImage"
/>
<p className="postContent__username">{postData.username}</p>
</Link>
</div>
<div className="postComments" />
<div className="postInfo">
<div className="postActions">
{liked ? (
<Tooltip title="Unlike post">
<Icon
type="heart"
className="likePost"
theme="filled"
style={{ color: "#d41c00" }}
onClick={() => this.props.unlikePost(postData.id)}
/>
</Tooltip>
) : (
<Tooltip title="Like post">
<Icon
type="heart"
className="likePost"
onClick={() => this.props.likePost(postData.id)}
/>
</Tooltip>
)}
<Tooltip title="Comment">
<Icon type="message" className="commentButton" />
</Tooltip>
</div>
<Tooltip title="Refresh comments">
<Icon type="reload" className="reloadComments" />
</Tooltip>
</div>
<div />
</div>
</div>
)}
</div>
);
}
}
const mapStateToProps = state => {
return {
postDetails: state.posts.postDetails
};
};
const mapDispatchToProps = dispatch => {
return {
getPostData: postId => dispatch(getPostData(postId)),
likePost: postId => dispatch(likePost(postId)),
unlikePost: postId => dispatch(unlikePost(postId))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PostDetails);
postsReducer.js
const initialState = {
creatingPost: false,
feed: [],
createdPost: false,
feedUpdated: false,
postDetails: {}
};
const postsReducer = (state = initialState, action) => {
switch (action.type) {
case "CREATING_POST":
return {
...state,
creatingPost: true,
createdPost: false
};
case "ADD_POST":
return {
...state,
feed: state.feed.concat(action.payload)
};
case "FETCH_FEED":
return {
...state,
feed: action.payload
};
case "CREATED_POST":
return {
...state,
creatingPost: false,
createdPost: true
};
case "UPDATE_FEED":
return {
...state,
feed: action.payload,
feedUpdated: true
};
case "GET_POST_DATA":
return {
...state,
postDetails: action.payload
};
case "RESET_FEED_UPDATED":
return {
...state,
feedUpdated: false
};
case "RESET_CREATED_POST":
return {
...state,
createdPost: false
};
case "LIKED_POST":
const { postDetails } = state;
postDetails.liked = true;
postDetails.likes += 1;
return {
...state,
postDetails: postDetails
};
case "UNLIKED_POST":
const postDetails1 = state.postDetails;
postDetails1.liked = false;
postDetails1.likes -= 1;
return {
...state,
postDetails: postDetails1
};
case "CLEAR_POST_DATA":
return initialState;
default:
return state;
}
};
export default postsReducer;
postsActions.js
import Axios from "axios";
import moment from "moment";
import store from "../store";
export const createPost = postData => {
return (dispatch, getState) => {
dispatch({ type: "CREATING_POST" });
Axios.post("/api/post/new", {
imageUrl: postData.imageUrl,
caption: postData.caption
})
.then(res => {
dispatch({ type: "CREATED_POST" });
dispatch({ type: "ADD_POST", payload: res.data.post });
})
.catch(err => {
console.log(err);
});
};
};
export const fetchFeed = () => {
return (dispatch, getState) => {
Axios.get("/api/user/feed")
.then(res => {
var feed = res.data.feed;
const state = store.getState();
const likedPosts = state.user.userData.likedPosts;
for (var i = 0; i < feed.length; i++) {
for (var j = 0; j < feed.length - i - 1; j++) {
if (moment(feed[j + 1].createdAt).isAfter(feed[j].createdAt)) {
var temp = feed[j];
feed[j] = feed[j + 1];
feed[j + 1] = temp;
}
}
}
for (var i = 0; i < feed.length; i++) {
if (likedPosts.indexOf(feed[i]._id) > -1) {
feed[i]["liked"] = true;
} else {
feed[i]["liked"] = false;
}
}
console.log(feed);
dispatch({ type: "FETCH_FEED", payload: feed });
})
.catch(err => {
console.log(err);
});
};
};
export const likePost = postId => {
return (dispatch, getState) => {
Axios.put("/api/post/like", { postId: postId })
.then(res => {
const feed = store.getState().posts.feed;
feed.forEach(post => {
if (post._id === postId) {
post.liked = true;
}
});
dispatch({ type: "UPDATE_FEED", payload: feed });
dispatch({ type: "LIKED_POST", payload: res.data.postId });
})
.catch(err => {
console.log(err);
});
};
};
export const unlikePost = postId => {
return (dispatch, getState) => {
Axios.put("/api/post/unlike", { postId: postId })
.then(res => {
const feed = store.getState().posts.feed;
feed.forEach(post => {
if (post._id === postId) {
post.liked = false;
}
});
dispatch({ type: "UPDATE_FEED", payload: feed });
dispatch({ type: "UNLIKED_POST", payload: res.data.postId });
})
.catch(err => {
console.log(err);
});
};
};
export const getPostData = postId => {
return (dispatch, getState) => {
Axios.get(`/api/post/${postId}`)
.then(res => {
const likedPosts = store.getState().user.userData.likedPosts;
if (likedPosts.indexOf(postId) > -1) {
res.data.post["liked"] = true;
} else {
res.data.post["liked"] = false;
}
dispatch({ type: "GET_POST_DATA", payload: res.data.post });
})
.catch(err => {
console.log(err);
});
};
};
export const resetFeedUpdated = () => {
return (dispatch, getState) => {
dispatch({ type: "RESET_FEED_UPDATED" });
};
};
export const resetCreatedPost = () => {
return (dispatch, getState) => {
dispatch({ type: "RESET_CREATED_POST" });
};
};
Your LIKED_POST and UNLIKED_POST reducer cases are not pure - they are are mutating the existing postDetails object in the state and putting it back into state so connect is optimizing and not re-rendering when it does a shallow equals comparison on postDetails from the previous and next props in componentShouldUpdate. Make sure you're creating a completely new value for postDetails like:
case "LIKED_POST":
const { postDetails } = state;
const newPostDetails = {
...postDetails,
liked: true,
likes: postDetails.likes + 1,
};
return {
...state,
postDetails: newPostDetails
};
You should check, if the comparison if (prevProps.postDetails !== this.props.postDetails) ever hits. Because with the like function you only change properties of the same object, the comparison will fail, because it's still the same object reference for postDetails. Try to return a new object in your reducer:
case "LIKED_POST":
const { postDetails } = state;
postDetails.liked = true;
postDetails.likes += 1;
return {
...state,
postDetails: {
...postDetails
},
}
Also if you're not changing anything of the object inside the component but in Redux store why not use the component property directly? You can remove the state object and the componentDidUpdate. Also you could refactor it to a function component.
render() {
const { postDetails: postData } = this.props;
...
}
When working with Redux, never forget the three principles
Single Source of truth
State is ready only
Reducers must be pure functions: Reducers take previous state and some action and modifies it and returns new state. We should never mutate state. We should create new objects and return them.
You have mutated existing state in your reducer functions. This doesnt trigger componentdidupdate because, connect method ( it checks mapStateToProps) treats that there is nothing that changed (It checks reference and since reference didnt change Component is not invoked).
You can use Object.assign or use spread operator which helps to make your reducers return a new object.
Change your Liked and unlinked posts reducer functions to return a new object instead of mutating existing object.
#azundo added how your code should be to achieve what you need.
I have a list of products called work items stored on my Redux store and I want to add an action that adds new work item or remove existing one when user picks up a a work item from the ui.
What I have so far is this workItemReducer:
import {
FETCH_WORKITEMS_BEGIN,
FETCH_WORKITEMS_SUCCESS,
FETCH_WORKITEMS_FAILURE,
SELECTED_WORKITEM
} from '../actions/workItemAction';
const initialState = {
workItems: [{"name":'work 1'}, {"name":'work 2'}, {"name":'work 3'}],
workItemsSelected: {},
loading: false,
error: null
};
export default function workItemReducer(state = initialState, action) {
switch(action.type) {
case FETCH_WORKITEMS_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_WORKITEMS_SUCCESS:
return {
...state,
loading: false,
workItems: action.payload.workItems
};
case FETCH_WORKITEMS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
workItems: []
};
case SELECTED_WORKITEM:
return {
...state,
workItemsSelected: action.payload.workItem
};
default:
return state;
}
}
and the actions looks as below:
export const FETCH_WORKITEMS_BEGIN = 'FETCH_WORKITEMS_BEGIN';
export const FETCH_WORKITEMS_SUCCESS = 'FETCH_WORKITEMS_SUCCESS';
export const FETCH_WORKITEMS_FAILURE = 'FETCH_WORKITEMS_FAILURE';
export const SELECTED_WORKITEM = 'SELECTED_WORKITEM';
export const fetchWorkItemsBegin = () => ({
type: FETCH_WORKITEMS_BEGIN
});
export const fetchWorkItemsSuccess = workItems => ({
type: FETCH_WORKITEMS_SUCCESS,
payload: { workItems }
});
export const fetchWorkItemsFailure = error => ({
type: FETCH_WORKITEMS_FAILURE,
payload: { error }
});
export const selectedWorkItem = workItem => ({
type: SELECTED_WORKITEM,
payload: { workItem }
});
I have a container component that disptach or call these actions which I am a bit confused where the logic of adding a new one or removing existing one happens, either on the container/smart component or directly in the reducer.
Container component has this method:
onWorkItemSelect = (workItem) => {
this.props.dispatch(selectedWorkItem(workItem));
};
Anyone can help on writing the logic of adding new or remove existing one and where that code should live?
adding this to reducer works thou im not sure if all this code should remain into the reducer:
case SELECTED_WORKITEM:
let arr = [];
if (containsObject(action.payload.workItem, state.workItemsSelected)) {
arr = remove(state.workItemsSelected, action.payload.workItem);
} else {
arr = [...state.workItemsSelected, action.payload.workItem];
}
return {
...state,
workItemsSelected: arr
};
It should be done in the reducer
when adding one you could just spread the current array which you can get from the reducer state
const { workItems } = state;
const { workItem } = action.payload;
return {
// ...other stuff to return
workItems: [...workItems, workItem],
}
to delete one
const { workItems } = state;
const { workItem } = action.payload;
return {
// ...other stuff to return
workItems: workItems.filter(x => x.name === workItem.name),
}
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.