state variables are coupled with reducer names in redux - reactjs

This might be a really silly one, but I was hanging my head around it for a while. I have a React project ( with Redux ). In mapStateToProps, state value is coming as undefined if I try to access the state directly as
const mapStateToProps = state => ({ data: state.data });
Instead, I always have to specify my reducer name ( the reducer which handles this particular state in it ) to access the state value :
const mapStateToProps = state => ({ data: state.customReducer.data });
Here is my code :
import { combinedReducer } from 'redux;
import customReducer from './customReducer';
const rootReducer = combineReducer({
customReducer
});
export default rootReducer;
customReducer.js : as follows
const initialState = {};
const customReducer = ( state = initialState, action ) => {
const { type, payload } = action;
switch (type) {
case 'SOME_ACTION':
return {
...state,
data: payload.data
}
break;
default:
return state;
}
}
export default customReducer;
store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
const configStore = () => {
return createStore (
rootReducer,
applyMiddleware(thunk)
);
}
const store = configStore();
export default store;
Does anyone know what is going wrong with the implementation ? Or is it the way to access different state values from different store ?
How can I directly access any state variable as
data: state.`state_value` , instead of , data : state.`reducerName`.`state_value` ?
Any help on this would be much appreciated.

There's nothing wrong with your implementation, this is just the way that combineReducers works. The reducer names are used to partition your store (e.g. Everything in your users reducer will be under state.users).
If you don't want to have to use the name of the reducer in your mapStateToProps, you can use selectors. However, in the selector, you will still have to use state.reducerName.
If you really don't want to have to go through more than one layer to get to the value you want, you could create a separate reducer for each value (i.e. In your example, the reducer would be named data). This obviously isn't the preferred way of doing it though.

Well, combineReducers isn't really using the reducer name, but the property name you specify for it. If you do:
const rootReducer = combineReducer({
foo: customReducer,
});
You'll be able to access the data at state.foo.data.

Related

react native Error: Actions must be plain objects. Instead, the actual type was: 'string'. You may need to add middleware

am using React redux in react native but facing this error. i have followed many solutions but no one worked for me, hers is more detail about my actions and reduces and store
action.js
export const AddNow =()=>{
return "ADD_NUM"
}
export const DelNow =()=>{
return "DEL_NUM"
}
in reducer folder -> MyBook.js
const initialState = 30;
const MyBook = (state = initialState, action) => {
switch (action.type) {
case "ADD_NUM":
return initialState +1;
case "DEL_NUM":
return initialState -1;
}
return state;
};
export default MyBook
in redcures folder index.js
import MyBook from "./MyBook";
import {combineReducers} from 'redux';
const rootreducers=combineReducers({ cartIMyBooktems})
export default rootreducers;
in store ->index.js
import {createStore,applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import rootreducers from '../reducers/index';
export default store = createStore(rootreducers, applyMiddleware(thunk));
in my app.js
import { AddNow , DelNow} from "../../actions";
import {useDispatch} from 'react-redux'
const dispatch = useDispatch();
<CustomBTN
onPress={()=>{dispatch(AddNow) }}
/>
i created this and followed one video tutorial but its not working for me but working for him. can anyone please help me? am beginner
The error says it all.
Your dispatch action is dispatching a string
export const AddNow =()=>{
return "ADD_NUM" //this action dispatches a string (returns a string)
}
this is causing "actual type was string"
It tells you, it is expecting a plain object. A plain object with a type property is also expected in your reducers
///
switch (action.type)
///
It is expecting a plain object with type property.
update your action.js to
action.js
export const AddNow =()=>{
return {
type: "ADD_NUM"
}
}
export const DelNow =()=> {
return {
type: "DEL_NUM"
}
}
now both your actions are returning plain objects, with a type property, as expected by redux dispatch and your reducers
also, since AddNow and DelNow are functions, they should be camelCase.
addNow, delNow
With regards to your book state always being 31.
in your reducers, you are always using the initial value as the modified value. The reducers recieve the current state as as the first parameter state which is defualted to initialState
update your reducers to use the state parameter
const initialState = 30;
const MyBook = (state = initialState, action) => {
switch (action.type) {
case "ADD_NUM":
return state +1; //update initialState to state
case "DEL_NUM":
return state -1; //update initialState to state
}
return state;
};
export default MyBook

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 state gets overwritten when calling 2 API using useEffect

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.

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?

Redux Reducer that simply returns an action's value and not a state?

I have a Container, an actionsCreator, and a reducer. In the below code, what's enabling the Reducer to return action.text instead of an updated state object? I thought reducers had to always return states.
HelloWorldContainer.jsx
import { connect } from 'react-redux';
import HelloWorld from '../components/HelloWorld';
import * as actionCreators from '../actions/helloWorldActionCreators';
const mapStateToProps = (state) => ({ name: state.name });
export default connect(mapStateToProps, actionCreators)(HelloWorld);
helloWorldActionCreators.jsx
import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
export const updateName = (text) => ({
type: HELLO_WORLD_NAME_UPDATE,
text,
});
helloWorldReducer.jsx
import { combineReducers } from 'redux';
import { HELLO_WORLD_NAME_UPDATE } from '../constants/helloWorldConstants';
const name = (state = '', action) => {
switch (action.type) {
case HELLO_WORLD_NAME_UPDATE:
return action.text
default:
return state;
}
};
const mainReducer = combineReducers({ name });
export default mainReducer;
(Code source: React on Rails).
The name is just a slice of state. And action.text is the updated state.
After combineReducers({ name }), the state tree looks like:
{
name: '..'
}
Besides, redux doesn't limit you that you can only use object as your state. If you pass name to createStore() directly without combineReducers, your entire state will become a plain string.
I thought reducers had to always return states.
No. Reducer has to always return data. Moreover, you should not return state, but a new object (or other data types).
So what is done in your case is that the reducer returns a new string (or whatever data type is text) every time HELLO_WORLD_NAME_UPDATE action is dispatched. It does not care what was already in the state and returns a new text string.

Resources