Why isn't combineReducers receiving state in Redux? - reactjs

When I look at a reducer being called via combineReducers, it's not getting the state or action in the arguments.
My reducers file is this:
import { combineReducers } from 'redux';
import nav from './reducers/nav';
import pages from './reducers/pages';
import entities from './reducers/entities';
export default function(initialData) {
return function(state, action) {
if (!state) {
console.log('no state', initialData);
return initialData;
}
// This is showing state as having expected value
console.log('state', state);
return combineReducers({
nav,
pages,
entities
});
}
};
My store initialization is like this:
import reducerWrapper from './reducers';
// Initial state is defined above
var reducers = reducerWrapper(initialState),
store = Redux.createStore(reducers, initialState);
The example code on the site doesn't use a wrapper (which I had seen in some other example). I tried that too and it didn't work. I mean in either example I'm not sure how it would get state/action given what is written out. I feel like I'm missing some magic here.
Updated
Here is the reducers file now:
import { combineReducers } from 'redux';
import nav from './reducers/nav';
import pages from './reducers/pages';
import entities from './reducers/entities';
export default combineReducers({
nav,
pages,
entities
});
followed by store = Redux.createStore(reducerWrapper, initialState), also doesn't work, even when I remove the wrapping inside reducerWrapper and just export default combineReducers`

The answer wasn't easily seen here. The problem is that my reducer did not have a default state.
The store triggers an INIT event here: https://github.com/reactjs/redux/blob/master/src/createStore.js#L204
It then eventually gets to here:
https://github.com/reactjs/redux/blob/master/src/combineReducers.js#L52
Meaning if my reducer is function (state, action) { return state } rather than function (state='anyDefaultValue') { return state }, combineReducer will error saying that the reducer did not return state

combineReducers returns a function that needs to be invoked with state and action. You can just export that directly:
const rootReducer = combineReducers(...);
export default rootReducer;
Or you can wrap it as you currently are:
const rootReducer = combineReducers(...);
export default function (initialData) {
return function (state, action) {
if (!state) {
console.log('no state', initialData);
return initialData;
}
// This is showing state as having expected value
console.log('state', state);
return rootReducer(state, action);
}
}

Related

Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers. i am facing this issue

store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers,
Here My store and root reducer file
import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";
import rootReducer from "./rootReducer";
const middlewares = [logger];
const store = createStore(rootReducer);
export default store;
This is the first time I'm working with React and Redux. I haven't been able to find error around it.
the above code is a total redux setup please help why this error coming:
import { combineReducers } from "redux";
import cardReducer from "../Services/profileimages/reducer";
export default combineReducers(cardReducer);
and my reducer file
import initialState from "./initialState";
import * as types from "./actionTypes";
const cardReducer = ({ state = initialState, action }) => {
switch (action.type) {
case types.FIREBASE_GETTING_IMAGES:
return {
cards: action.cards,
};
default:
return state;
}
};
export default cardReducer;
Haven't been able to find anything around here regarding this error:
import initialState from "./initialState";
import * as types from "./actionTypes";
const cardReducer = ({ state = initialState, action }) => {
switch (action.type) {
case types.FIREBASE_GETTING_IMAGES:
return {
cards: action.cards,
};
default:
return state;
}
};
export default cardReducer;
the above code is a total redux setup please help why this error coming
From the API doc combineReducers(reducers), the reducers parameter should be
An object whose values correspond to different reducing functions that need to be combined into one. See the notes below for some rules every passed reducer must follow.
Therefore, the correct usage is:
import { combineReducers } from 'redux';
import cardReducer from './cardReducer';
export default combineReducers({
card: cardReducer,
});
Besides, the cardReducer has the wrong signature. It should be:
const cardReducer = (state = initialState, action) {}

Store does not have a valid reducer?

This is first time i'm working with React and Redux. I haven't able to find error around it.
"webpack-internal:///./node_modules/react-error-overlay/lib/index.js:1446 Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers."
This is reducer:
import { combineReducers } from 'redux';
const rootReducer = combineReducers({});
export default rootReducer;
If the object is empty.
const rootReducer = combineReducers({});
This error will show.
add some data in it like:
import { combineReducers } from 'redux';
import foo from '../foo' //this is your reducer
const rootReducer = combineReducers({foo});
export default rootReducer;
reducer example
//reducer/foo.js
export default function foo(state = null, action) {
switch (action.type) {
case 'MY_ACTION_TYPE':
return action.payload
}
return state;
}

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?

State is empty in reducer

I working on a react-redux webapp and until now, all action were related to ajax requests, so I'm using redux thunk and axios.
Now, I'm working in a component that would create a list from objects called 'tasks'. This should be very straight forward: the action calls send the task object and the reducer add it in an array inside the 'tasks' state.
The issue is that when called the reducer always rewrite the the tasks object array. Doing a console.log at the beginning of the reducer call I see the state is always empty.
Here is the code:
import {DROP_TASK} from '../actions/types'
export default function (state = {}, action) {
switch (action.type){
case(DROP_TASK):
console.log("state", state); // here is shows object{}
console.log('tasks', state.tasks);
if(state.tasks === undefined || state.tasks === null ) {
return {...state, list: [action.payload]}
}
return {...state, list: [...state.tasks.list, action.payload]};
default:
return state
}
}
at the createStore at the index.js
I'm doing:
const createStoreWithMiddleware = applyMiddleware(reduxThunk, logger)(createStore);
where logger just console.log the whole state before and after every dispatch. So I know that before the dispatch for the reducer function, the "tasks" is populated after a first call.
Here is how I'm combining reducers:
import { combineReducers } from 'redux';
import {reducer as form} from 'redux-form';
import {reducer as uiReducer} from 'redux-ui';
import authReducer from './auth_reducer';
import treeReducer from './tree_reducer';
import media_asset_list_reducers from './media_asset_list_reducers';
import tasks from './tasks';
const reducers = combineReducers({
form: form,
auth: authReducer,
tree: treeReducer,
media_asset_list: media_asset_list_reducers,
tasks: tasks,
ui: uiReducer
});
export default reducers;
Can anyone help me?
Thanks
You arenot showing how are you combining reducers. But it seems to me that half of your reducer is written to get substate in form tasks.list, but it returns new state in form just list.
If you compose reducers with combineReducers, each of the combined reducers gets just its subpart of state (and that is what it should return as well)
Try:
import {DROP_TASK} from '../actions/types'
export default function (state = {}, action) {
switch (action.type){
case(DROP_TASK):
return {...state, list: [...state.list, action.payload]};
default:
return state
}
}
Your reducer will receive the state.tasks slice of the state already, so there is no need to include .tasks in this reducer.
This will result in a state tree like:
{
... //other reducers
tasks: {
list: []
}
}
If you want to remove the list layer and just have tasks be a list you can change the reducer to:
import {DROP_TASK} from '../actions/types'
export default function (state = [], action) {
switch (action.type){
case(DROP_TASK):
return [...state, action.payload];
default:
return state
}
}
Also, DROP_TASK seems like an odd action type for adding an item to list... but not knowing more about your app, this may still be appropriate, so ignore this if so.

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