Redux state gets overwritten when calling 2 API using useEffect - reactjs

I have 2 actions that calls different API. I dispatch these actions in a useEffect.
I have 2 reducers files, One for each, to store the data received from the API.
So, Basically I should be able to access both the data individually using useState.
But the secondly called API's data is overwriting the data of the first API. I don't understand how, because they are not even on the same file or even related.
Component
const items = useSelector((state) => state.lostchambers.items);
const lostChambersItems = useSelector((state) => state.sharklostdolsea.itemsLostChamber);
useEffect(() => {
dispatch(fetchingLostChambers());
dispatch(fetchSharkLostDolSea());
}, [dispatch]);
The Action for both the files looks like this I'm only posting here for one file as its the same code
import { FETCH_POSTS } from "./type";
import axios from "../../utils/Request";
export const fetchingLostChambers = () => async (dispatch) => {
const response = await axios.get("API");
const { data = false, status } = response;
if (status === 200) {
if (data) {
dispatch({
type: FETCH_POSTS,
items: data.acf,
});
}
}
};
The Reducer for both the actions looks like this but I'm only posting here for one file as its the same code
import { FETCH_POSTS } from "../actions/lostchambers/type";
const initialState = {
items: [],
};
export default (state = initialState, action) => {
switch (action.type) {
case FETCH_POSTS:
return {
...state,
...action,
};
default:
return state;
}
};
Combined Reducer
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import rootReducers from "./reducers";
const initialState = {};
const middleware = [thunk];
const store = createStore(rootReducers, initialState, applyMiddleware(...middleware));
export default store;
RootReducer
import { combineReducers } from "redux";
import venues from "./venues";
import lostchambers from "./lostchambers";
import sharklostdolsea from "./sharklostdolsea";
export default combineReducers({
venues,
lostchambers,
sharklostdolsea,
});
Am I missing something here? I just can't figure out the issue ,I'v been trying four hours now.

The main issue I see here is that you are using the same type constant for both actions and reducers.
The way redux works is that it will pass the actions through all of the reducers that are combined together and will run whatever state changes the reducer says happens. That's why when you set up reducers you need the base case to return state if nothing matches.
By using the same type in the actions, both reducers will see both actions that were dispatched and perform the change. So a race condition occurs and the last one that is returned shows in both parts of state.
You should be able to fix this by just changing the action and reducer type constant for one/both of them.

Related

Empty Redux store when attempting to dispatch from outside React component

I have what I think is a fairly standard React-Redux setup with some persistence mixed in.
app.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import AppRouter from './routers/AppRouter';
import configureStore from './store/configureStore';
import config from 'cgConfig';
const { store, persistor } = configureStore();
const jsx = (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<AppRouter />
</PersistGate>
</Provider>
);
ReactDOM.render(jsx, document.getElementById(config.constants.element.rootId));
configureStore.js
import {
createStore,
combineReducers
} from 'redux';
import {
persistStore,
persistReducer
} from 'redux-persist';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import storage from 'redux-persist/lib/storage';
import config from 'cgConfig';
//...import reducers...
export default () => {
const persistConfig = {
key: 'root',
storage,
stateReconciler: autoMergeLevel2
};
let rootReducer = combineReducers({
//...all of my reducers...
});
let store = undefined;
let pr = persistReducer(persistConfig, rootReducer);
if (config.env.includeReduxDevTools) {
store = createStore(
pr,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
} else {
store = createStore(pr);
}
let persistor = persistStore(store);
return {
store,
persistor
};
};
However, the one thing that is a bit unconventional is that I need to update the store outside the context of a react component. As far as I understand (for example), this should not be too difficult to accomplish. Simply call configureStore() to get the store and run a store.dispatch(action). Problem is that I keep getting the initial state of the store back (IE empty), which isn't the same as the one I have already set up through the process of logging in etc. Thus when I run a dispatch, the wrong state is being updated.
Not sure what I am doing wrong, would appreciate any help.
EDIT to answer Uma's question about what the router looks like:
First some more context. The website I am working on will basically have shapes generated in something akin to a graph. These shapes can be manipulated after they are selected from various sources like contextual menus and a toolbar. It is in the context of this change event that I am working on.
When a change is made, the info of the selected item and what needs to be changed will be sent to a generic function which will determine which reducer/action to use to update the Redux store. All my reducers follow the same pattern and look like this:
const reducerInitialState = {};
const setSomeProperty= (state, action) => {
let newState = { ...state };
newState.some_property = action.some_update;
return newState;
};
export default (state = reducerInitialState, action) => {
switch (action.type) {
case 'UPDATE_SOME_PROPERTY':
return setSomeProperty(state, action);
case 'LOG_OUT':
return { ...reducerInitialState };
default:
return state;
}
};
Action for completeness:
export const setSomeProperty = update_data => ({
type: 'UPDATE_SOME_PROPERTY',
some_update: update_data
});
The generic function I have would look something like this:
import configureStore from './store/configureStore';
import { setSomeProperty} from './actions/SomeAction';
import API from './utilities/API';
export default async ({ newValue, selectedShape }) => {
if (!newValue || !selectedShape)
throw "Cannot update the server with missing parameters.";
await API()
.post(
`/api/update-shape`,
{
newValue,
shape: selectedShape
}
)
.then(response => {
updateValue({ selectedShape, newValue });
})
.catch(error => {
// handle error
});
}
const getAction = ({ shape }) => {
switch (shape.type) {
case 0:
return setSomeProperty;
default:
throw `Invalid type of ${shape.type} attempting to update.`;
}
}
const updateValue = ({ selectedShape, newValue }) => {
const action = getAction({ shape: selectedShape })
const { store } = configureStore();
store.dispatch(action(newValue))
}
Note: API is a wrapper for Axios.
Since posting this yesterday I have read that creating a second store like what I am doing with const { store } = configureStore(); is where one of my problems lie in that React/Redux can't have 2 of them. I have also come to realize that the problem most likely have to do with the initial state in my reducers, that somehow using configureStore() does not send the actual state to the reducers and thus all my reducers are showing their initial states when I look at them using console.log(store.getState());. If this is true, at least I know the problem and that is half the battle, but I am unsure how to proceed as I have tried to ReHydrate the state I get from configureStore() but nothing seems to work the way I expect it to.
As far as I can tell you end up in a weird loop where you call an action setSomeProperty then it gets you to the reducer, and in reducer you call that setSomeProperty action again.
In reducer I would expect to see something like this:
export default (state = reducerInitialState, action) => {
switch (action.type) {
case 'UPDATE_SOME_PROPERTY':
return {
...state, // preserve already set state properties if needed
some_update: action.some_update
}
case 'LOG_OUT':
return { ...reducerInitialState };
default:
return state;
}
};

What is the recommended way of accessing a variable from a different reducer?

What is the recommended way of accessing a variable from a different reducer?
import { createStore, combineReducers } from 'redux';
import mainReducer from './reducers/main';
import configReducer from './reducers/config';
const rootReducer = combineReducers({
main:mainReducer,
config:configReducer
});
const store = createStore(rootReducer);
export default store;
I have two different reducer and I have an action inside mainReducer where I want to access a variable inside configReducer. What is the proper way to do this with Redux?
export default function (state, action) {
switch (action.type) {
case "UPDATE_ACTIVE":
//need to check variable options inside configReducer
default:
return state;
}
}
Basically a reducer just returns a slice of the global state i.e store.
If you are using a middleware like thunk, you can share the data like so:
export function updateActive(params) {
return (dispatch, getState) => {
const { config } = getState(); // the part you want to access.
dispatch({
type: UPDATE_ACTIVE,
options: config.options,
params,
});
};
}
So you get all the data in the action itself and then dispatch with the ACTION_TYPE.
You can read more on sharing state between reducers on :
https://redux.js.org/faq/reducers/
https://redux.js.org/api/store#getstate

Redux store not updating by action

I have setup a reducer in redux to to manage state. I am calling that reducer through an action creator. The action is dispatching (coz I hava subscribed to the store and put a console.log there)
This is what the reducer looks like
import C from "../constants"
import { combineReducers } from 'redux'
export const wishlist = (state = [], action) => {
switch (action.type) {
case C.REMOVE_PRODUCT_FROM_WISHLIST:
return state.filter(product => product.ProductName !== action.payload.ProductName)
default:
return state
}
}
export default combineReducers({
wishlist
})
Like I said, I have hooked it up with an action creator which I know is firing (again, coz the store is subscribing)
This is what the action creator looks like
import C from './constants'
export const removeProductFromWishlist = function(ProductName) {
return {
type: C.REMOVE_PRODUCT_FROM_WISHLIST,
payload: ProductName
}
}
Is there something I am missing here?
for reference, here is the initialState.json
{
"wishlist": [
],
}
Please let me know what am I doing wrong here?

Redux store changes connected component props without corresponding action being dispatched

I have a very weird issue, I have redux store and a react component connected to it with connect() function. I am storing a list of user roles coming from my backend in redux. For this I have two different reducers, userRoleReducer and initialUserRolesReducer. I use the first one to handle changes into the roles in the UI before applying the changes with an API call, and the second one is being used to have the initial roles stored separately after backend responses. The issue I am having, is that both of the reducers are changing, even though only the first one is actually being updated by dispatching an action (Sorry if my use of terms is incorrect). Below are the reducers and action dispatchers.
Reducers:
export function userRolesForUsersRequestSuccess(state = {userRoles: []}, action) {
switch(action.type) {
case 'USER_ROLES_FOR_USERS_REQUEST_SUCCESS':
return action.userRoleDataForUsers;
default:
return state;
}
}
export function initialUserRolesForUsersRequestSuccess(state = {userRoles: []}, action) {
switch (action.type) {
case 'INITIAL_USER_ROLES_FOR_USERS_REQUEST_SUCCESS':
return action.initialUserRoleData;
default:
return state;
}
}
These are the action dispatchers, the first one is called from the connected component, and and after backend response. The second one is called only after the backend response.
export function setUserRolesForUsersRequestSuccess(userRoleDataForUsers) {
return {
type: 'USER_ROLES_FOR_USERS_REQUEST_SUCCESS',
userRoleDataForUsers
};
}
export function setInitialUserRolesForUsersRequestSuccess(initialUserRoleData) {
return {
type: 'INITIAL_USER_ROLES_FOR_USERS_REQUEST_SUCCESS',
initialUserRoleData
};
}
I haven't found anything similar to this from anywhere, so I guess this isn't a common problem, and that's why a good guess is that the issue is in my code. But every other reducer I use are working just fine, and believe me, I have tried to change and check everything I can to make these two work normally as well.
Any help is wanted to track the issue down!
EDIT: The code I use to create the store, not sure if it helps.
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './rootReducer';
import createHistory from 'history/createBrowserHistory';
const composeEnhancers = typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const history = createHistory();
const middleware = routerMiddleware(history);
const initialState = {};
const store = createStore(
rootReducer,
initialState,
composeEnhancers(
applyMiddleware(middleware, thunk))
);
EDIT 2. rootReducer.js file, reducers are combined here.
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import {
userRoleForMeRequestSuccess,
userRolesForUsersRequestSuccess,
userRoleRequestPending,
userPermissionChangeGetResponseMessage,
initialUserRolesForUsersRequestSuccess } from './Common/userRoleReducer';
const appReducer = combineReducers({
userRoleForMeRequestSuccess,
userRolesForUsersRequestSuccess,
userRoleRequestPending,
userPermissionChangeGetResponseMessage,
initialUserRolesForUsersRequestSuccess,
router: routerReducer
});
const rootReducer = (state, action) => {
if (action.type === LOGIN_LOGOUT_REQUEST_SUCCESS) {
state = undefined;
}
return appReducer(state, action);
};
export default rootReducer;
EDIT 3. After I dug more deeply into this problem, I made an observation that if I just pass completely different data for the first reducer, its data stays intact and doesn't change when the other one changes. So could there be some kind of issue in passing exactly the same data as the first new state after the reducers initial state, and that mixes the reducers somehow to always mirror each other?

Loading Data from MongoDB into Redux Store fetching from my node API

I am working on my very first react app, and I have successfully setup my Node API and MongoDB, and am now trying to integrate redux into my application. I will try to share the relevant code snippets here.
First, in my node API, I have a model mlb_ids_logos_colors.js with some baseball data. My react app is currently getting this data using the following fetch using an async function:
export async function get_mlb_ids_logos_colors() {
return fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json();
})
}
I would like to get the data from this endpoint into my redux store, and then from the redux store into the various components that will use it, but I am a bit stuck. As far as redux, I have the following files:
reducers/index.js (my rootReducer)
import { combineReducers } from 'redux';
import mlbIdsLogosColorsReducer from './mlb-ids-logos-colors-reducer';
export default combineReducers({
mlbIdsLogosColorsReducer
})
reducers/mlb-ids-logos-colors-reducer.js
export default function reducer (state={
mlbIdsLogosColorsData: [],
}, action) {
switch (action.type) {
case "FETCH_COLORS": {
return {...state, mlbIdsLogosColorsData }
}
}
return state;
}
actions/mlb-ids-logos-colors-action.js
export const FETCH_COLORS = "FETCH_COLORS";
export function fetchMlbIdsLogosColors() {
return function(dispatch) {
dispatch({type: "FETCH_COLORS"});
fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json();
})
}
}
lastly, I setup my store in **store.js* as follows, and import this into my apps main index.js file:
store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
Any help with this is appreciated. For reference, I am receiving the following error when launching the app:
./src/reducers/mlb-ids-logos-colors-reducer.js
Line 7: 'mlbIdsLogosColorsData' is not defined no-undef
I'm aware this is a quite-obvious error message, but admittidly I'm not sure where I should be defining mlbIdsLogosColorsData.
Thanks in advance!
EDIT: I don't have to make any changes ever to the data at my /mlb/mlb_ids_logos_colors endpoint. I just want to get this data into the redux store, and then from the redux store into the components. I know to use mapStateToProps() and connect() in my components to get the data into the components.
EDIT2: I HAVE DIFFERENT NAMES FOR THE ACTION! let me fix that, and see if that resolves the issue!
I'm in a hurry sorry if I misleading you but roughly you are dispatching an action without data. You should use something like that in your action creator:
export function fetchMlbIdsLogosColors() {
return function(dispatch) {
fetch('/mlb/mlb_ids_logos_colors')
.then(resp => {
return resp.json()
.then( json => dispatch({type: "FETCH_COLORS", json}));
})
}
}
and then use this payload (json) in your reducer like that:
export default function reducer (state={
mlbIdsLogosColorsData: [],
}, action) {
switch (action.type) {
case "FETCH_COLORS": {
return {...state, mlbIdsLogosColorsData: action.json }
}
}
return state;
}
Again, this is a rough suggestion. I did not check your whole code. But you are getting undefined error since there is not a variable named mlbIdsLogosColorsData right now.

Resources