React+Redux - Uncaught Error: Expected the reducer to be a function - javascript

I tried simple react,redux,ajax working example and followed Reddit API tutorial, but I cannot create store and get error:
Uncaught Error: Expected the reducer to be a function.
index.jsx
...
import { createStore, applyMiddleware } from 'redux'
var thunkMiddleware = require('redux-thunk');
var createLogger = require('redux-logger');
var rootReducer = require('./reducers.js');
const loggerMiddleware = createLogger();
function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(
thunkMiddleware,
loggerMiddleware
)
)
}
const store = configureStore();
...
rootReducer.js
import { combineReducers } from 'redux';
function products(state = {
isFetching: false,
didInvalidate: false,
items: []
}, action) {
switch (action.type) {
case 'REQUEST_PRODUCTS':
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case 'RECEIVE_PRODUCTS':
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
})
default:
return state
}
}
function specialPosts(state = { }, action) {
switch (action.type) {
case RECEIVE_SPECPOSTS:
case REQUEST_SPECPOSTS:
return Object.assign({}, state, {
req: true
})
default:
return state
}
}
const rootReducer = combineReducers({
products,
specialPosts
});
export default rootReducer;
Type of rootReducer is object, but why? Should I change createStore function to rootReducer.default?
return createStore(
rootReducer.default,
initialState,
applyMiddleware(
thunkMiddleware,
loggerMiddleware
)
)
package.json
"redux-logger": "^2.6.1",
"react-redux": "^4.4.1",
"react-redux-provide": "^5.2.3",
"redux": "^3.3.1",
"redux-thunk": "^2.0.1",

const rootReducer = combineReducers({
products,
specialPosts
});
const store = createStore( rootReducer, applyMiddleware( thunkMiddleware ));
The initial state is then created automatically from the initial states returned by the individual reducer functions. These individual states can be accessed as state.products and state.specialPosts

The problem was due to rootReducer being imported by "require" (ES5):
var rootReducer = require('./reducers.js');
If you import it via the ES6 method, it will correctly save the rootReducer.js' default automatically into rootReducer as you expected:
import rootReducer from './reducers';
I see you are mixing ES5 (require) and ES6 (import) in that file...I was mixing in my project as well, which is why I ran into this problem. More information can be found here: https://medium.com/#kentcdodds/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution-ad2d5ab93ce0#.2x2p2dx3m

When you create store first argument must be function that means reduce(()=>[]) parameter
Error:
import {createStore,applyMiddleware} from 'redux';
export default function configureStore(initialState) {
return createStore(
[],
{},
applyMiddleware()
);
}
Solution:
import {createStore,applyMiddleware} from 'redux';
export default function configureStore(initialState) {
return createStore(
()=>[],
{},
applyMiddleware()
);
}

When you see the error:
Uncaught Error: Expected the reducer to be a function
It does not necessarily mean that the reducer is not a function. If you experience this error, you should try creating the store with just the reducer. So like this:
createStore(reducer)
Which might be working.
In my case, the second parameter was wrong, because I didn't call compose and applyMiddleware in the correct order. So this is a correct setup that seems to have worked in my case:
export const createDebugStore = () => {
const options: EnhancerOptions = {}
const composeFunc = (typeof window !== 'undefined' && composeWithDevTools(options)) || compose;
const enhancer = composeFunc(applyMiddleware());
return createStore((state: any) => state || {}, enhancer);
}

rootReducer
/* index.js */
import AppReducer from './reducers';
// import {AppReducer} from './reducers';
// bugs : must be a root reducer, can not be use as a module
/* reducers.js */
// initial state
const initialState = {
angular: 0,
react: 0,
vuejs: 0
};
// reducers update state
const AppReducers = (state = initialState, action) => {
switch (action.type) {
case 'VOTE_ANGULAR':
console.log("Vote Angular!");
return (
Object.assign(
{},
state,
{
angular: state.angular + 1
}
)
);
case 'VOTE_REACT':
console.log("Vote React!");
return (
Object.assign(
{},
state,
{
react: state.react + 1
}
)
);
case 'VOTE_VUEJS':
console.log("Vote Vue.jsc!");
return (
Object.assign(
{},
state,
{
vuejs: state.vuejs + 1
}
)
);
default:
return state;
}
};
export {AppReducers};
export default AppReducers;

I use VS Code Insiders and sometimes changes are not saved correctly. So if you followed all of the above and the error is still there go to each file and press STRG+S. It solved the issue for me.

i have faced the same problem and it is solved by
const redux = require('redux');
const store = redux.createStore(reducer);
and my reducer is
const reducer = (state = initailState, action)=>{
if(action.type === 'BUY_CAKE'){
return {...state, numOfCake : state.numOfCake + 1}
}
if(action.type === BUY_ICECREAM){
return {...state, numOfIceCream : state.numOfIceCream + 1}
}
}

Related

Unable to access Reducer & state values

I am unable to access my state values saved in store on any screen. values reach to actions but when I access it from store it returns always undefined.
Every thing is in separate files
Reducer 1
import * as Actions from '../actionTypes'
import initialStore from './initialStore'
const homeModuleReducer = (state = initialStore, action) => {
switch (action.type) {
case Actions.SET_PROFILE_ONE:
console.log('call here')
return {
...state,
currentUser: action.profile
}
default:
return state;
}
}
export default homeModuleReducer
Reducer 2
import * as Actions from '../actionTypes'
import initialStore from './initialStore'
const loginModuleReducer = (state = initialStore, action) => {
switch (action.type) {
case Actions.SET_PROFILE:
return {
...state,
currentUser: action.profile
}
case Actions.SET_INITIAL_LOADING_STATUS:
return {
...state,
isInitialLoadingDone: action.loadingStatus
}
default:
return state;
}
}
export default loginModuleReducer
Combine Reducer
import { combineReducers } from 'redux'
import homeModuleReducer from './homeModuleReducer'
import loginModuleReducer from './loginModuleReducer'
export default combineReducers({
homeModuleReducer,
loginModuleReducer,
})
Store
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk';
import rootReducer from './reducers'
let store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
export default store;
usage:
const mapStateToProps = (state) => ({
stateLoaded: state.rootReducer.isInitialLoadingDone,
profile: state.rootReducer.currentUser
});
Error:
undefined is not an object (evaluating 'state.rootReducer.isInitialLoadingDone')
You already combined your reducers so you can access reducer by it's key like this :
const mapStateToProps = (state) => ({
stateLoaded: state.homeModuleReducer.isInitialLoadingDone, // here homeModuleReducer is just an example. Change with reducer key in which isInitialLoadingDone is belong
profile: state.loginModuleReducer.currentUser
});
With hooks its much easier
wrap your root with store
import {Provider} from 'react-redux';
const App = () => {
return (
<Provider store={store}>
<Yourcomponent/>
</Provider>
);
};
export default App;
Access your state in any component like this
import { useSelector } from "react-redux";
const state = useSelector(state => state)

Why React Redux dispatch twice when fire

The State
The tracing results
Each time when I click the button, the dispatch run twice, like the picture above.
This is the AppBar component and mapDispatchToProps function.
const mapStateToProps = state => {
return {
title: state.title
};
};
const mapDispatchToProps = {
onClick: () => {
return {
type: "TOGGLE_SIDEBAR"
};
}
};
const AppBar = props => (
<Box>
<Button icon={<Notification />} onClick={props.onClick} />
</Box>
);
const AppBatContainer = connect(
mapStateToProps,
mapDispatchToProps
)(AppBar);
export default AppBatContainer;
This is the reducer
import {
TOGGLE_SIDEBAR
} from "../constants/action-types";
const initialState = {
showBar: false
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case TOGGLE_SIDEBAR:
return Object.assign({}, state, {
showBar: !state.showBar
});
default:
return state;
}
};
export default rootReducer;
This is the store.js
import { createStore, applyMiddleware, compose } from "redux";
import reduxThunk from "redux-thunk";
import rootReducer from "./reducers/index";
const composeEnhancers =
typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
trace: true,
traceLimit: 20
})
: compose;
const enhancer = composeEnhancers(applyMiddleware(reduxThunk));
const store = createStore(rootReducer, enhancer);
export default store;
All libraries are well imported. I tried remove redux-thunk, it is still the same result.
Thanks in advance.
Ref
React-Redux Counter example action firing twice I have viewed this questions on stackoverflow, and tried the answers, but no of them solved it.

Using Immutable JS and Redux - action is undefined

I am using Redux and Immutable JS.
I set up the store like this
import { combineReducers } from 'redux-immutable';
...
const rootReducer = combineReducers({});
import { initialState } from '../reducers/...';
export const store = createStore(
combineReducers(rootReducer, initialState),
composeEnhancers(
applyMiddleware(...middleware)
)
);
Now I get the following error
// reducers/.../initialState.js
export function foo(state = initialState, action) {
switch (action.type) {
...
...
TypeError: Cannot read property 'type' of undefined
and it highlights switch (action.type) {.
When I am not using redux-immutable and set up my store like this
import { ..., combineReducers } from 'redux';
export const store = createStore(
combineReducers({ initialState }),
composeEnhancers(
applyMiddleware(...middleware)
)
);
I do not get an error. I do not understand why it says that action.type is undefined. Any ideas?
combineReducers only takes reducers:
const yourRootReducer = combineReducers({ someReducer, someOtherReducer })
It doesn't take initialState, your individual stores can (and createStore though you usually don't need to there).
Your call to createStore should be:
const store = createStore(
rootReducer,
initialState, // can be undefined
composeEnhancers(
applyMiddleware(...middleware)
)
);
Assuming middleware is an array.
I think your code should look like this
export const store = createStore(
rootReducer, initialState,
composeEnhancers(
applyMiddleware(...middleware)
)
);
// I removed the combineReducers()
Reference: https://github.com/gajus/redux-immutable#usage
Example:
import {
combineReducers
} from 'redux-immutable';
import {
createStore
} from 'redux';
const initialState = Immutable.Map();
const rootReducer = combineReducers({}); // we use the combineReducers() here
const store = createStore(rootReducer, initialState); // and don't need to use it here
Your initialState should not be a Reducer.
You should only combine all your Reducers.
Your initial State should be an object (State).
e.g.
{
loaded: true,
someDataString: ''
}

Redux Reducer is returning an [Object Object] , However i want string?

I am trying to change state of a const in redux.
i am trying my dispatch directly in the component i want to change the state in. The State is changed after dispatch,However i get back an Object. And when i Console.log , i get [Object][Object] , which before calling dispatch i used to get the Value of the state.
This is my Store.
import { createStore,applyMiddleware , compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
//const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
This is my Main Reducer.
import { combineReducers } from 'redux';
import sidebarReducer from './sidebarReducer';
export default combineReducers({
name : sidebarReducer
});
This is my CustomReducer , which i call sidebarReducer.
import { TOGGLE_SIDEBAR } from '../actions/types';
let sidebarname = "wrapper slide-menu";
export default function(state=sidebarname,action){
switch(action.type){
case TOGGLE_SIDEBAR:
console.log('reducer called');
console.log(state);
return{
...state,
sidebarname : action.payload
};
}
return state;
}
This is my Dispatch and MapStatetoProps Function.
const mapStatetoProps = state => ({
name : state.name
});
const mapDispatchtoProps = dispatch => ({
setName : (name) => {
dispatch({
type: "TOGGLE_SIDEBAR",
payload: name
})
}
})
export default connect(mapStatetoProps,mapDispatchtoProps)(App);
I successfully retrieved the State from the store , however when i dispatch i get back an Object.
sidebarReducer.js:13 reducer called
sidebarReducer.js:14 wrapper slide-menu
App.js:38 sidebarname is [object Object]
App.js:40 wrapper slide-menu
In handling your action, you are returning an object (check the curly braces):
return {
...state,
sidebarname: action.payload
};
Since your entire state is only the string sidebarname, you should return only the payload:
return action.payload
Alternatively, you can have your state be an object, and then your action return should work just fine:
let initialState = { sidebarmenu: "wrapper slide-menu" };
...
export default function(state=initialState,action){
...
}
your sidebarReducer just manages a string. just return action.payload on TOGGLE_SIDEBAR, not an object with a sidebarname property.

Default parameters are injected as object in babeljs/reactjs?

My reactjs setup with babeljs is show below
My action.js
export function onIncrement() {
return {
type: 'INCREMENT'
};
}
export function onDecrement() {
return {
type: 'DECREMENT'
};
}
container/App.js
import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as CounterActions from '../actions';
class App extends Component {
render() {
const { counter, actions } = this.props;
return (
<div>
<p>
Clicked: {counter} times
</p>
<p>
<button onClick={actions.onIncrement}>
+
</button>
</p>
<p>
<button onClick={actions.onDecrement}>
-
</button>
</p>
</div>
);
}
}
App.propTypes = {
counter: PropTypes.number.isRequired,
actions: PropTypes.object.isRequired
};
function mapStateToProps(count) {
return {
counter: count
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(CounterActions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
containers/root.js
import React, { Component, PropTypes } from 'react';
import { Provider } from 'react-redux';
import App from './App';
export default class Root extends Component {
render() {
const { store } = this.props;
return (
<Provider store={store}>
<App />
</Provider>
);
}
}
Root.propTypes = {
store: PropTypes.object.isRequired
};
Reducer/index.js
export default function counter(count = 0, action) {
console.log(count) // this comes as object {}
console.log(action) // initially it as { type: '##INIT'}
switch (action.type) {
case 'INCREMENT':
return count + 1;
case 'DECREMENT':
return count - 1;
default:
return count;
}
}
store/configureStore.js
import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState = {}) {
// thunkMiddleware to handle async actions in redux
const middlewares = [thunkMiddleware];
// chrome devtool extension
let devtool;
if (NODE_ENV === 'development') {
// redux-logger to log the redux state events in window.console
const logger = createLogger({
duration: true
});
middlewares.push(logger);
// devtools - redux-chrome extension
devtool = window.devToolsExtension ? window.devToolsExtension() : undefined;
}
// store - combines reducers and enchancements to redux using middlewares
const store = createStore(
rootReducer,
initialState,
devtool,
applyMiddleware(...middlewares)
);
// hot module replacement for only for reducers
if (module.hot) {
module.hot.accept('../reducers', () => {
// default - as es6 to es5 transpile in babel make the module to export as
// module.export = somemodule.default
const nextRootReducer = require('../reducers').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
}
my main.js
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import Root from './containers/Root';
import configureStore from './store/configureStore';
const initialState = window.__INITIAL_STATE__;
const store = configureStore(initialState);
render(
,
document.getElementById('root')
);
My Package.json
& i am using webpack for bundling and babel for transpiling.
When i initially run this application on i get this error below,
Further investigate this issue, i found the transpiled code as show below
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = counter;
function counter() {
var count = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];
var action = arguments[1];
console.log(count, "count"); // this comes as object {}
console.log(action, "action"); // initially it as { type: '##INIT'}
switch (action.type) {
case 'INCREMENT':
return count + 1;
case 'DECREMENT':
return count - 1;
default:
return count;
}
}
My Question is:
Why my defualt parameter is injected as Object {} ?
Is this babel problem ?
Am i doing anything wrong here?
The above code only works, when i change this to below,
export default function counter(count = 0, action) {
console.log(count , "count") // this comes as object {}
console.log(action, "action") // initially it as { type: '##INIT'}
switch (action.type) {
case 'INCREMENT':
return count + 1;
case 'DECREMENT':
return count - 1;
default:
return 0;
}
}
Check your configureStore function:
export default function configureStore(initialState = {}) {
// ...
}
You setting initialState to {}, so if window.__INITIAL_STATE__ is undefined you will get {} as initial state of your reducer.
Try to change this to:
export default function configureStore(initialState = 0) {
// ...
}
There is no problems with babel output.
future:
That empty object injection is due to i am setting the initialState to = {} in configureStore.js, i changed that to undefined, it worked like a charm
import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
// thunkMiddleware to handle async actions in redux
const middlewares = [thunkMiddleware];
// chrome devtool extension
let devtool;
if (NODE_ENV === 'development') {
// redux-logger to log the redux state events in window.console
const logger = createLogger({
duration: true
});
middlewares.push(logger);
// devtools - redux-chrome extension
devtool = window.devToolsExtension ? window.devToolsExtension() : undefined;
}
// store - combines reducers and enchancements to redux using middlewares
const store = createStore(
rootReducer,
initialState,
devtool,
applyMiddleware(...middlewares)
);
// hot module replacement for only for reducers
if (module.hot) {
module.hot.accept('../reducers', () => {
// default - as es6 to es5 transpile in babel make the module to export as
// module.export = somemodule.default
const nextRootReducer = require('../reducers').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
}
For more details:
https://github.com/reactjs/redux/issues/1502#issuecomment-194151490
In your mapStateToProps function, you first need to extract count from Redux state like this :
function mapStateToProps(state) {
return {
counter: state.count
};
}

Categories

Resources