When I try to call any other action in redux it is setting one part of the state to it's initialState.
My root reducer looks like this
const rootReducer = combineReducers({
credentials: combineReducers({
cred,
user,
partner,
merchant,
bank,
error,
auth,
}),
preCredentials,
theme,
});
the part of the state that is being cleared is theme.
action dispatched
state diff
Why this actions that have anything to do with theme reducer can change its state.
Theme reducer
function theme(state = { ...INITIAL_THEME }, action) {
switch (action.type) {
case LOADING_THEME:
return {
...state,
isLoading: true,
};
case SAVE_THEME:
return {
...action.theme,
error: {
status: null,
message: '',
},
isLoading: false,
};
case CLEAR_THEME:
return INITIAL_THEME;
default:
return INITIAL_THEME;
}
}
reducer of dispatched action
function preCredentials(state = { ...INITIAL_STATE }, action) {
switch (action.type) {
case SAVE_USERNAME:
return { ...state,
user: { ...state.user,
fullName: action.fullName,
},
};
default:
return state;
}
}
function theme(state = { ...INITIAL_THEME }, action) {
switch (action.type) {
case LOADING_THEME:
return {
...state,
isLoading: true,
};
case SAVE_THEME:
return {
...state,
...action.theme,
error: {
status: null,
message: '',
},
isLoading: false,
};
case CLEAR_THEME:
return INITIAL_THEME;
default:
return state;
}
}
return state instead of initial state
Related
Any idea why my reducer is acting on action.type based on its position in the switch statement?
I spent a lot of time debugging and then realized it was actually the position in the switch statement that is causing the problem.
import {
FETCH_PRODUCTS_BEGIN,
FETCH_PRODUCTS_SUCCESS,
FETCH_PRODUCTS_ERROR,
CREATE_PRODUCT,
UPDATE_PRODUCT,
DELETE_PRODUCT,
} from "./inventoryTypes";
const initialState = {
loading: false,
products: [],
error: "",
};
const userInventoryReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_PRODUCTS_BEGIN:
return {
...state,
loading: true,
};
case FETCH_PRODUCTS_SUCCESS:
return {
loading: false,
products: action.payload,
error: "",
};
case FETCH_PRODUCTS_ERROR:
return {
loading: false,
products: [],
error: action.payload,
};
case DELETE_PRODUCT:
return {
...state,
products: state.products.filter(
(product) => product._id !== action.payload
),
};
case CREATE_PRODUCT:
console.log("your calling create!");
return {...state}
case UPDATE_PRODUCT:
console.log("calling update");
return { ...state };
default:
return state;
}
};
Check the values inside your "./inventoryTypes" file' maybe there are duplications of the same actions.
This is resolved. The value for my CREATE_PRODUCT, UPDATE_PRODUCT inside "./inventoryTypes" is the same!
I am trying to do an API call through reducer.My code is working fine here but the problem is all the actions, reducers are inside same file. So I tried to separate the reducer and actions in different file, but it is not working. I have debugged by putting some console.log but it is not helping me. Can anyone tell me how to fix it? I am providing my code snippet and sandbox below.
https://codesandbox.io/s/redux-async-actions-os6nu
import {
SELECT_CHANNEL,
REQUEST_POSTS,
RECEIVE_POSTS,
DISPLAY_ALERT
} from "../actions";
//const reducer = (state = {}, action) => {
const fetchReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_DATA_START:
return {
...state,
fetching: true,
fetchingMessage: "fetch data start"
};
case RECEIVED_DATA:
return {
...state,
fetching: false,
fetched: true,
data: action.payload,
fetchingMessage: "received data"
};
case FETCH_DATA_ERROR:
return {
...state,
fetching: false,
error: action.payload,
fetchingMessage: "fetch data error"
};
default:
return state;
}
};
export default fetchReducer;
You need to initialize your state in the main reducer file as well as use the same action name across all your application. I have also used mapStateToProps and mapDispatchToProps methods to make it easy and simple to use redux with react (https://redux.js.org/basics/usage-with-react).
Reducer file
import { combineReducers } from "redux";
import { REQUEST_POSTS, RECEIVE_POSTS } from "../actions";
const initialState = {
fetching: false,
fetched: false,
data: [],
error: null,
fetchingMessage: ""
};
const fetchReducer = (state = initialState, action) => {
switch (action.type) {
case REQUEST_POSTS:
return {
...state,
fetching: true,
fetchingMessage: "fetch data start"
};
case RECEIVE_POSTS:
return {
...state,
fetching: false,
fetched: true,
data: action.payload,
fetchingMessage: "received data"
};
case "FETCH_DATA_ERROR":
return {
...state,
fetching: false,
error: action.payload,
fetchingMessage: "fetch data error"
};
default:
return state;
}
};
export default combineReducers({ posts: fetchReducer });
App.js
const mapStateToProps = state => {
return {
data: state.posts
};
};
const mapDispatchToProps = dispatch => {
return {
onFetch: () => {
dispatch(requestPosts);
},
onFetchSuccess: data => {
dispatch(receivedPosts(data));
}
// onFetchError: () => {
// // dispatch()
// }
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
Here is the working demo link forked from your CodeSandbox (https://codesandbox.io/s/redux-async-actions-436qu). Let me know if you do not understand anything.
I have a user store that handles all the user actions, which include fetching user data and updating user information. I've also got a post store that fetches posts users have written and also, posts, updates, and deletes them.
When I update my user information and call my function:
export const putUserDataPending = () => {
return {
type: "PUT_USER_DATA_PENDING"
}
}
export const putUserDataFulfilled = user => {
return {
type: "PUT_USER_DATA_FULFILLED",
user: user
}
}
export const putUserDataRejected = error => {
return {
type: "PUT_USER_DATA_REJECTED",
error: error
}
}
export const putUserData = (username, email, countries, home) => {
const token = localStorage.getItem('token');
return dispatch => {
dispatch(putUserDataPending());
axios.put(
'http://localhost:8000/api/v1/rest-auth/user/',
{
username: username,
email: email,
countries: countries,
home: home
},
{headers: { 'Authorization': `Token ${token}`}}
)
.then(response => {
const user = response.data;
dispatch(putUserDataFulfilled(user));
})
.catch(err => {
dispatch(putUserDataRejected(err));
})
}
}
It rerenders the user profile page, but it doesn't rerender the author information because the state for the posts was never updated. What's the proper way to update any of the author objects in the posts state that may have been updated by updating the user?
Or is it just better to manually rerender the homepage?
My home page MapState is this:
const mapState = state => {
return {
fetched: state.tripReport.fetched,
fetching: state.tripReport.fetching,
tripReports: state.tripReport.tripReports,
showCountryModal: state.modal.showCountryModal,
modalCountry: state.modal.modalCountry
};
}
where tripReports is an array of objects, and some of the authors I want to update.
My editProfile MapState looks like this:
const mapState = state => {
return {
user: state.user.user,
userCountries: state.user.user.countries,
searchedCountry: state.country.country,
showProfileModal: state.modal.showProfileModal,
modalProfile: state.modal.modalProfile
};
}
Where the user object is the object getting updated.
Here is the post reducer:
const initialState = {
fetching: false,
fetched: false,
fetchingTripReports: false,
fetchedTripReports: false,
tripReports: [],
userTripReports: [],
error: null,
}
export default function (state = initialState, action) {
switch (action.type) {
case "FETCH_TRIP_REPORTS_PENDING": {
return {
...state,
fetching: true
}
}
case "FETCH_TRIP_REPORTS_FULFILLED": {
return {
...state,
fetching: false,
fetched: true,
tripReports: action.tripReports
}
}
case "FETCH_TRIP_REPORTS_REJECTED": {
return {
...state,
fetching: false,
fetched: false,
error: action.error
}
}
case "FETCH_USER_TRIP_REPORTS_PENDING": {
return {
...state,
fetchingTripReports: true
}
}
case "FETCH_USER_TRIP_REPORTS_FULFILLED": {
return {
...state,
fetchingTripReports: false,
fetchedTripReports: true,
userTripReports: action.tripReports
}
}
case "FETCH_USER_TRIP_REPORTS_REJECTED": {
return {
...state,
fetchingTripReports: false,
fetchedTripReports: false,
error: action.error
}
}
case "POST_TRIP_REPORTS_PENDING": {
return {
...state,
}
}
case "POST_TRIP_REPORTS_FULFILLED": {
return {
...state,
// The new trip report must be added onto the array, then the array must be sorted by id.
userTripReports: [...state.userTripReports].concat(action.response).sort((a, b) => a.id < b.id),
tripReports: [...state.tripReports].concat(action.response).sort((a, b) => a.id < b.id)
}
}
case "POST_TRIP_REPORTS_REJECTED": {
return {
...state,
error: action.error
}
}
case "DELETE_TRIP_REPORTS_PENDING": {
return {
...state,
}
}
case "DELETE_TRIP_REPORTS_FULFILLED": {
return {
...state,
// The deleted post must be filtered out of the lists.
userTripReports: state.userTripReports.filter(tripReport => tripReport.id !== action.response.id),
tripReports: state.tripReports.filter(tripReport => tripReport.id !== action.response.id)
}
}
case "DELETE_TRIP_REPORTS_REJECTED": {
return {
...state,
error: action.error
}
}
case "UPDATE_TRIP_REPORTS_PENDING": {
return {
...state,
}
}
case "UPDATE_TRIP_REPORTS_FULFILLED": {
return {
...state,
// The old post must be filtered out, the updated post must be added, then the array must be sorted.
userTripReports: [...state.userTripReports].filter(tripReport => tripReport.id !== action.response.id).concat(action.response).sort((a, b) => a.id < b.id),
tripReports: [...state.tripReports].filter(tripReport => tripReport.id !== action.response.id).concat(action.response).sort((a, b) => a.id < b.id),
response: action.response
}
}
case "UPDATE_TRIP_REPORTS_REJECTED": {
return {
...state,
error: action.error
}
}
default:
return state
}
}
and here is the user reducer:
const initialState = {
fetching: false,
fetched: false,
adding: false,
added: false,
user: {},
error: null,
}
/* Reducer Function*/
export default function (state = initialState, action) {
switch (action.type) {
case "FETCH_USER_PENDING": {
return {
...state,
fetching: true
}
}
case "FETCH_USER_FULFILLED": {
return {
...state,
fetching: false,
fetched: true,
user: action.user
}
}
case "FETCH_USER_REJECTED": {
return {
...state,
fetching: false,
fetched: false,
error: action.error
}
}
case "PUT_USER_DATA_PENDING": {
return {
...state,
adding: true,
added: false,
}
}
case "PUT_USER_DATA_FULFILLED": {
return {
...state,
adding: false,
added: true,
user: action.user
}
}
case "PUT_USER_DATA_REJECTED": {
return {
...state,
adding: false,
added: false,
error: action.error
}
}
default:
return state
}
}
Where on the action 'PUT_USER_DATA_FULFILLED' I would like to update the tripReport state to rerender the homepage.
I have an authReducer with and the User state.
I also have a generalReducer with state relating to error notification message and loading true/false.
How do I, when loading the login or another task in a seperate reducer, modify or make a global loading state variable from one reducer that is relating to another reducer?
generalReducer.js:
let defaultState = {
loading: false,
error: null,
notification: false,
};
export default function generalReducer(state = defaultState, action){
switch(action.type){
case loading:
return {
...state,
loading: action.payload.isLoading,
};
default:
return state;
}
authReducer.js
let defaultState = {
user: {
displayName: null,
email: null,
photoUrl: null,
isAnonymous: null,
phone: null,
}
};
export default function authReducer(state = defaultState, action){
switch(action.type){
case LOGIN_BEGIN:
case LOGOUT_BEGIN:
return {
...state,
};
case LOGIN_FAILURE:
case LOGOUT_FAILURE:
return {
...state,
};
case LOGIN_SUCCESS:
return {
...state,
user: action.payload.user
};
default:
return state;
}
}
In authReducer.js i want to be able to set the loading or error state in generalReducer to correspond to the error or loading state required. How?
You need to import authActionTypes from authActions and update the reducer in generalReducer.js
generalReducer.js:
import {
LOGIN_BEGIN,
LOGOUT_BEGIN,
LOGIN_FAILURE,
LOGOUT_FAILURE,
LOGIN_SUCCESS,
} from './authActions;
let defaultState = {
loading: false,
error: null,
notification: false,
};
export default function generalReducer(state = defaultState, action){
switch(action.type){
case loading:
return {
...state,
loading: action.payload.isLoading,
};
case LOGIN_BEGIN:
case LOGOUT_BEGIN: {
return {
...state,
loading: true,
error: null,
}
}
case LOGIN_FAILURE:
case LOGOUT_FAILURE: {
return {
...state,
loading: false,
error: 'Set Your Error Here',
}
}
case LOGIN_SUCCESS: {
return {
...state,
loading: false,
error: null,
}
}
default:
return state;
}
Hope this helps.
I'm building a quite simple app using Redux and my reducers all look alike. It works, technically, but that's a lot of code duplication.
// The employees reducer
export default (state = initialState, action) => {
switch (action.type) {
case EMPLOYEES_REQUEST:
return [ ...state, { isFetching: true } ]
case EMPLOYEES_SUCCESS:
// DEBUG
console.log('Dispatching employees');
console.log(action.response);
// END DEBUG
// Return employees directly in the employees state
return { ...state, list: JSON.parse(action.response) };
case EMPLOYEES_FAILURE:
return [ ...state, { isFetching: false } ]
default:
return state
}
}
And
// The services reducer
export default (state = initialState, action) => {
switch (action.type) {
case SERVICES_REQUEST:
return [ ...state, { isFetching: true } ]
case SERVICES_SUCCESS:
// DEBUG
console.log('Dispatching services');
console.log(action.response);
// END DEBUG
// Return services directly in the services state
return { ...state, list: JSON.parse(action.response) };
case SERVICES_FAILURE:
return [ ...state, { isFetching: false } ]
default:
return state
}
}
Is there something I can to to use a generic reducer with different actions?
Thanks!
Reducer is just a function. You could always use a higher order function to make it.
const makeListReducer = (initial, prefix) => (state = initial, action) => {
switch(action.type) {
case `${prefix}_REQUEST`: return {...state, isFetching: true}
case `${prefix}_SUCCESS`: return {...state, isFetching: false, list: JSON.parse(action.response)}
case `${prefix}_FAILURE`: return {...state, isFetching: false, /*etc*/}
}
return state
}
// The employees reducer
export default makeListReducer(initialState, 'EMPLOYEES')