Sparkmasterflex Sparkmasterflex - 4 months ago 37
CoffeeScript Question

redux Store does not have a valid reducer coffeescript

First time working with Redux and I understand the concepts of Actions and Reducers but cannot get them to play nicely in my code and I'm worried it's because I'm using CoffeeScript instead of ES6.

I think the issue is I can't do the

export default ...
in my /app/reducers/index.coffee but honestly Redux is very new to me and I've been stumbling along with webpack and react for a while and am still not the most comfortable with either, so ¯\_(ツ)_/¯

I keep getting the error:

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


/app/actions/index.coffee:

{ FETCH_ITEMS, FETCH_SINGLE_ITEM } = require('../constants')

fetch_items = ->
{ type: FETCH_ITEMS }


fetch_single_item = (id) ->
{
type: FETCH_SINGLE_ITEM,
id: id
}

module.exports = { fetch_items, fetch_single_item }


/app/reducers/items.coffee:

$ = require('jquery')
{ FETCH_ITEMS, FETCH_SINGLE_ITEM } = require('../constants')
{ fetch_items } = require('../actions')

INITIAL_STATE = []

items = (state=INITIAL_STATE, action) ->
console.log action.type
switch action.type
when FETCH_ITEMS
console.log 'FETCH_ITEMS'
$.get "http://dosomething.com/items", (resp) ->
console.log resp
when FETCH_SINGLE_ITEM
console.log 'do something'
else
console.log "suckit turk!"
state

module.exports = { items }


/app/reducers/index.coffee:

{ combineReducers } = require('redux')
items = require('./items')

reducers = combineReducers({ items })

module.exports = { reducers }


and finally /app/index.cjsx

React = require("react")
ReactDOM = require("react-dom")
{
createStore,
combineReducers,
appyMiddleware
} = require('redux')

{ reducers } = require("./reducers/")

STORE = createStore combineReducers({ reducers, routing: routerReducer })
HISTORY = syncHistoryWithStore(browserHistory, STORE)

STORE.subscribe () ->
console.log STORE.getState()

...omitted react render: ->...


EDIT

Sorry everyone, yes routeReducer is defined, I just omitted it because I'm pretty sure that it's not the problem.

/app/index.cjsx

React = require("react")
ReactDOM = require("react-dom")
{
createStore,
combineReducers,
appyMiddleware
} = require('redux')

{
syncHistoryWithStore,
routerReducer
} = require('react-router-redux')


{ reducers } = require("./reducers/")

STORE = createStore combineReducers({ reducers, routing: routerReducer })
HISTORY = syncHistoryWithStore(browserHistory, STORE)

STORE.subscribe () ->
console.log STORE.getState()

...omitted react render: ->...

Answer

A reducer must:

  1. Receive a state and an action as argument
  2. Always return the same or a new state.

Your "reducer" items only appears to return a state in case none of the cases in the switch statement match, which means it is not a valid reducer. If you are encountering this error when you emit one of those actions, redux could detect that a new state hasn't been returned. See whether you still encounter this issue after making items a proper reducer.

Side Note

You are making an asynchronous API call from your items reducer, and your stub code looks like you are planning to add a second one. This is not the correct use of a reducer; the store should hold data pertaining to your application state, and a reducer should manufacture a new state given an old state and an action type and/or payload.

Instead, call these sorts of asynchronous functions in an "action creator". (See docs.)

An action creator like fetch_items will execute the AJAX request, and on success trigger a dispatch of a set_items action, e.g. { type: 'SET_ITEMS', payload: [...response.items...] }. Your reducer will listen for the SET_ITEMS action and return a new state, updated with the payload.

Comments