Redux state doesn't update with action dispatch
I haven't run your code, so I can't say offhand if just this will fix it, but your username reducer is returning an object with a username
property, when it should be returning the action.username
string.
const username = (state = '', action) => {
switch (action.type) {
case types.UPDATE_USERNAME:
return action.username
default:
return state;
}
};
Also, have you verified that you don't have a typo in your types
declaration? I see that in your reducer you reference types.UPDATE_USERNAME
, but in your action creator you set the type using the string UPDATE_USERNAME
.
DGaffneyDC
Updated on June 05, 2022Comments
-
DGaffneyDC almost 2 years
I've gone through every piece of documentation and example project I can find for building a react app with redux and react-router, but I just can't seem to figure out how to get my redux state to update when I dispatch actions. As you can see in this screenshot, actions are dispatching properly, but my store/nextState isn't updating.
ACTION:
export function updateUsername(username) { return { type: types.UPDATE_USERNAME, username }; }
REDUCER (EDIT: I've tried both of these variations):
/* first variation */ const username = ( state = '', action, ) => { switch (action.type) { case types.UPDATE_USERNAME: return Object.assign({}, state, { username: action.username, }); default: return state; } }; /* second variation */ const username = ( state = '', action, ) => { switch (action.type) { case types.UPDATE_USERNAME: return action.username; default: return state; } };
REDUCER COMBINATION:
const user = combineReducers({ isAuthenticated, token, password, username, }); export default user;
REDUCERS/INDEX.JS:
const rootReducer = combineReducers({ isFetching, open, search, user, routing: routerReducer, }); export default rootReducer;
STORE CONFIGURATION:
import React from 'react'; import { createStore, compose, applyMiddleware } from 'redux'; import createLogger from 'redux-logger'; import thunkMiddleware from 'redux-thunk'; import { routerMiddleware } from 'react-router-redux'; import rootReducer from '../reducers'; import * as actions from '../actions'; function configureStore(history, initialState) { const loggerMiddleware = createLogger(); const enhancer = window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(); const store = createStore( rootReducer, initialState, compose( applyMiddleware( thunkMiddleware, loggerMiddleware, routerMiddleware(history), ), enhancer, ), ); if (module.hot) { // Enable Webpack hot module replacement for reducers module.hot.accept('../reducers', () => { const nextReducer = rootReducer; store.replaceReducer(nextReducer); }); } return store; } export default configureStore;
STORE CREATION:
const initialState = {}; const store = configureStore(browserHistory, initialState); const history = syncHistoryWithStore(browserHistory, store); const routes = createRoutes(store); render( <Provider store={store}> <Router history={history} routes={routes} /> </Provider>, document.getElementById('root'), );
AND FINALLY, THE COMPONENT:
import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import TextField from 'material-ui/TextField'; import validator from 'validator'; import className from 'classnames'; import { Link } from 'react-router'; import * as AuthActions from '../../actions/AuthActions'; class LoginForm extends Component { constructor(props) { super(props); this.handleUsernameChange = this.handleUsernameChange.bind(this); this.handlePasswordChange = this.handlePasswordChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleUsernameChange(e) { this.props.actions.updateUsername(validator.escape(e.target.value.trim())); } handlePasswordChange(e) { this.props.actions.updatePassword(validator.escape(e.target.value.trim())); } handleSubmit(e, getState) { e.prevent.default(); const user = { username: this.props.user.username, password: this.props.user.password }; console.log(user); this.props.actions.loginUser(user); } render() { return ( <form autoComplete="off" onSubmit={this.handleSubmit} className='login-form'> <TextField type="username" autoFocus="true" floatingLabelText="Username" floatingLabelFixed={true} autoComplete="off" onChange={this.handleUsernameChange} /> <br/> <TextField type="password" autoFocus="true" floatingLabelText="Password" floatingLabelFixed={true} autoComplete="off" onChange={this.handlePasswordChange} /> </form> ); } } function mapStateToProps(state) { return { user: state.user, }; } function mapDispatchToProps(dispatch) { return { actions: bindActionCreators(AuthActions, dispatch), }; } export default connect( mapStateToProps, mapDispatchToProps, )(LoginForm);
I've been stuck on this for a week now, so any help you can offer would be GREATLY appreciated! Thanks!
-
DGaffneyDC over 7 yearsUnfortunately, I've tried that format for the reducer, along with a number of other variations as well. None seem to be working. As far as the types declaration, I imported '* as types' in my reducer file. I'll try switching that up now.
-
Paul S over 7 yearsHave you thrown a
console.log
into yourusername
reducer to ensure that it is being called? (or just added a breakpoint in the devtools) -
DGaffneyDC over 7 yearsYup, it's firing. I'm getting a 200 from my backend authorization service when I dispatch my login action, too, but the same issue with nextState not updating.
-
Paul S over 7 yearsWell, I'm still not seeing anything. Have you tried stepping through the reducer returned by
combineReducers
with a debugger? github.com/reactjs/redux/blob/v3.6.0/src/… That should verify the return values of all of your reducer functions. -
DGaffneyDC over 7 yearsSo I found a bit of a work around, but it's throwing all sorts of eslint errors: By changing the "return action.username" to "return state = action.username", my state up updating with all dispatches. The error eslint is throwing though is "Return statement should not contain assignment". But hey, if it works it works...
-
İsmail Atkurt about 6 yearsYou should keep state mutable. Setting as "state=something" can change whole state of application. Instead, it is expected to return state from a reducer as "return Object.assign({}, state, action);"