2
votes

So I'm extremely new to Redux (this is my first project using it) and I've run into a bit of a roadblock.

What I have here is a simple scenario in which an input value changes, updating the Redux store onChange. Here's a look at my code:

The component:

import React from 'react';
import { connect } from 'react-redux';

import { setSuperhero } from '../actions/setSuperhero'

const SearchField = (props) => (
  <div className="d-flex justify-content-center">
    <form className="form-inline">
      <div className="form-group mb-0">
        <input
          type="text"
          name="superhero"
          className="form-control"
          placeholder="Thor, Iron Man, etc."
          onChange={(e) => {
            props.dispatch(setSuperhero(e.target.value));
          }}></input>
      </div>
      <button type="submit" className="btn btn-light ml-2">Search</button>
    </form>
  </div>
)

const mapStateToProps = state => {
  return {
    superhero: state.superhero.name
  }
}

export default connect(mapStateToProps)(SearchField);

The action:

import * as types from '../types';

export const setSuperhero = name => dispatch => (
  dispatch({
    type: types.SET_SUPERHERO,
    payload: name
  })
)

The types:

export const SET_SUPERHERO = "SET_SUPERHERO";

The reducer:

import * as types from '../types'

const superheroReducer = (state = {name: 'Thor'}, action) => {
  switch (action.type) {
    case types.SET_SUPERHERO: {
      return {
        ...state,
        name: action.payload
      }
    }
    default:
      return state;
  }
}

export default superheroReducer

Logger indicates that the action is sent, the state just doesn't update with the value provided. I've tried looking at immutability. I've tried making sure my Provider is set up properly (it does). I've used and deleted 'mapDispatchToProps' about 100 times. I've looked through every recommended question on this board for an answer and still come I'm coming up empty. For whatever reason, this stupid thing just won't update.

Any help at all would be immensely appreciated.

EDIT: As requested, here's the code for my root reducer:

import { combineReducers } from 'redux';

import uiReducer from './uiReducer';
import superheroReducer from './superheroReducer';
import heroInfoReducer from './heroInfoReducer';
import comicsReducer from './comicsReducer';
import seriesReducer from './seriesReducer';
import eventsReducer from './eventsReducer';

const rootReducer = combineReducers({
  ui: uiReducer,
  superhero: superheroReducer,
  heroInfo: heroInfoReducer,
  comics: comicsReducer,
  series: seriesReducer,
  events: eventsReducer
})

export default rootReducer

EDIT: Quick addendum on accepted answer: was calling 'thunk' AND 'promise' in my 'applyMiddleware' call. Removing 'promise' fixed the issue, but ultimately the problem was that I was passing in a function to my dispatch call. Thank you everyone!

1
Which state doesn't update? You're passing superhero prop to SearchField, so you can access it with props.superhero. Can you please try to console.log(props) in SearchField and start typing in the input? What's the log?Jordan Enev
Also can you please show us how you register the reducer?Jordan Enev
@JordanEnev Added the reducer registry. I also console.logged my reducer and it doesn't appear to be firing.Ian J Barker
Oh I see what's happening. You're returning function in your action. Dispatch expects an object.Jordan Enev
That was the trick! I had thunk installed, but having both 'thunk' and 'promise' in my 'applyMiddleware' call was causing some kind of roadblock.Ian J Barker

1 Answers

3
votes

Here everything looks correct except

1. setSuperhero action is returning a function which is incorrect. It should return a plain object. Quoting from the redux documentation

Actions are plain JavaScript objects. Actions must have a type property that indicates the type of action being performed.

So you need to change

export const setSuperhero = name => dispatch => (
  dispatch({
    type: types.SET_SUPERHERO,
    payload: name
  })
)

to

export const setSuperhero = name => ({
  type: types.SET_SUPERHERO,
  payload: name
});

2. You have not included the createStore part in the question. So if you want to access the subtree of state then you must register reducer at that tree using the combineReducers e.g. Here you want to access your state value at state.superhero. So you must register your reducer as such

import { createStore, combineReducers } from "redux";
import reducers from "./reducer";

const store = createStore(combineReducers({ superhero: reducers }));

export default store;

Now you can access it as state.superhero. If you don't want to use the combineReducers and just register a single reducer as

const store = createStore(reducers)

then the superhero name data will be available under state.name and not the state.superhero.name.

Here is the working codesandbox. https://codesandbox.io/s/n445j386xp

Update: As you have included the createStore part in the question now, The problem is with your action. It must return a plain object, not a function. Rest looks good.

Hope, it helps :)