Reputation: 2019
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.
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
Upvotes: 3
Views: 2617
Reputation: 11260
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:
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.
Upvotes: 6
Reputation: 17598
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.
Upvotes: 0