2
votes

I am new to Redux and i've been having a hard time rendering changes made to the store. I've been using Redux-DevTools to explore state changes and here is my problem.

I have a sidebar which has expanded state as true/false. Initial state is below.

{expanded: false}

During the toggle action i trigger store.dispatch to change the state to true. In React-DevTools i could see that my state is being changed, the console also logs the change when executed from within my sidenav component.

I have a home page which is able to fetch the initial state of the store, however the actions from sidenav which updates the store doesn't re-render(props val dint change) the home page.

I strongly feel the issue is related to this SO post but not able to get around the wrapped component concept. Any advice.

React Redux - changes aren't reflected in component

Below is code.

Edit p52155my9j

Redux Store

const rootReducer = combineReducers({
    sideBarReducerMain: sidebarReducer })

export const configurestore = () => {
    const store = createStore(rootReducer, composeWithDevTools(
      ));

    return store; }

Action generators

export const onExpand = {
    type: 'EXPAND',
    payload: {
        expanded: true
    }
}

export const onCollapse = {
    type: 'COLLAPSE',
    payload: {
        expanded: false
    }
}

Reducer

export const sidebarReducer = (state = {expanded:false},action) => {
 switch (action.type) {
     case 'EXPAND':
        return Object.assign({}, state, {expanded: true})
     case 'COLLAPSE':
        return Object.assign({}, state, {expanded: false})
    default:
        return state;   
 }
}

SideBar Toggle (The console logs the new state on every toggle)

onToggle = (expanded) => {

    expanded ? store.dispatch(onExpand):store.dispatch(onCollapse)
    this.setState({ expanded: expanded });

    //outputs the state change looks fine.
    console.log("State of store :: " + store.getState());

};

Home page

import React from 'react';
import styled from 'styled-components'
import Main from './MainAlign'
import { connect } from 'react-redux'

class HomePage extends React.Component {


 getExpansionState() {
  console.log("From render" + this.props.expandedState)
    return this.props.expandedState
 }

 componentWillReceiveProps(nextProps) {
  console.log("Looks like this never gets called :( " + this.props.expandedState)
}


  render(props) {

      return (
        <div>
        <Main expanded={this.getExpansionState()}>
        <h1>Welcome</h1>
        <p>This is my site. Take a look around!</p>
        </Main>  
        </div>
      )


  }

}

const mapStateToProps = state => {
  console.log("New state " + state.expanded)
  return {
    expandedState: state.expanded
  }
}

export default connect(mapStateToProps)(HomePage);

REDUX DevTools

ReduxDevTools

2
What is the output of console.log(this.props.expandedState) after your render but before your return in your HomePage component?devserkan
The value is undefinedSudheej
In mapStateToProps you are not accessing the name of the reducer. It should be state.sideBarRedycerMain.expendedSagiv b.g
Thanks Sagiv i fixed that also ensure that redux state is not mutated, the problem still exists :( . I could be missing bind action, but the action is actually from the side nav component which updates the state.Sudheej
I’ve updated sandbox in questionSudheej

2 Answers

6
votes

You need to declare action creators (functions) which return actions (objects):

export const onExpand = () => ({
  type: "EXPAND",
  payload: {
    expanded: true
  }
});

Then instead of calling store.dispatch, you should connect the action creator:

withRouter(connect(null, { onExpand, onCollapse })(SideBar))

And invoke it in your component:

if (expanded) {
  this.props.onExpand();
} else {
  this.props.onCollapse();
}

Working code:

Edit xj64o1j0kw

Misc

this.setState({ expanded: expanded });
console.log("FROM SIDENAV" + this.state.expanded);

setState is asynchronous so you need use the callback to access the updated value:

this.setState({ expanded }, () => console.log("FROM SIDENAV" + this.state.expanded));

Also, there's no need to replicate the redux state in local component (single source of truth).

You can also combine onExpand and onCollapse into a single onToggle function and perform the toggle in reducer: expanded: !state.expanded, or pass the expanded flag as payload.

0
votes

Change your mapStateToProps function like that:

const mapStateToProps = state => {
  console.log("New state " + state.expanded)
  return {
    expandedState: state.sideBarReducerMain.expanded
  }
}

You have a sideBarReducerMain state in your root,global state. This sideBarRecuerMain state has an expanded value not the global one.

Also, you don't need a payload for your action creators since you don't use them. Just a type is enough for your logic.