redux: MapStateToProps not being called even after changing STATE in reducer.
Do you want to request a feature or report a bug?
I am retrieving data from Salesforce using JS remote Actions and populating data in React JS.
The structure of my application:
My component1.js:
import React from 'react';
const component1 = React.createClass({
getInitialState() {
return {
basicData : {},
interestData : {},
accountingData : {},
isLoading : true
}
},
componentWillMount() {
console.log('componentWillMount');
},
componentDidMount() {
console.log('componentDidMount');
this.props.action1();
},
componentWillReceiveProps(NextProps) {
console.log('componentWillReceiveProps', NextProps);
},
componentWillUnmount() {
console.log('componentWillUnmount');
},
render() {
console.log('RENDERING');
return (
<div>
<p> Loading! </p>
</div>
)
}
});
export default component1;
This is my container.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import RootComponent from '../components/component1.js';
import { action1 } from '../actions/actionCreators';
function mapStateToProps(globalState) {
return {
value1: globalState.value1,
value2: globalState.value2
};
}
const mapDispatchToProps = (dispatch) => {
return {
getData: () => {
dispatch(getData())
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(component1);
Action creator:
export function action1() {
return {
type: 'RETRIEVE_REQUESTED_DATA',
value1: getRecords()
};
}
Reducer:
case "RETRIEVE_REQUESTED_DATA":
action.value1.then(function(data) {
return testVar = { ...state, basicData: state.basicData, interestData: data, accountingData: state.basicData, isLoading: false};
});
Though, I am returning altered state from my reducer, MapStateToProps in my container is not being invoked.
The server call returns a promise and that promise is handled in reducer here, once the data is retrieved then I am populating the data to my state.
My view to the issue,
Once data is retrieved from DB (Salesforce), state is altered thus, MapStateToProps should be invoked because it is connected to the Component with Connect.
Please shed some light.
I am very new to Redux and React not sure whether this is a issue or a usage question, pls excuse my ignorance.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 28 (18 by maintainers)
I just recently rewrote
connect
myself and had to learn it’s not as simple as one thinks. There’s a lot of edge cases and performance considerations that you will ultimately run into that are already handled for you withconnect
. Unless you have a compelling case against usingconnect
, reinventing the wheel now is just shooting yourself in the foot.Does the render function use this.state or this.props for data binding?
My understanding is that mapStateToProps() should replace the need for getInitialState(), and encourage use of this.props.
@jimbolla
Great, thanks for your clarification. I think what confused me is that when children get notified first, they don’t always rerender themselves first — in my experiments, the child didn’t render until after the parent even though the child received state updates first.
I believe this is related to the fact that React defers
setState
updates under certain conditions, and doesn’t defer under other conditions: http://www.bennadel.com/blog/2893-setstate-state-mutation-operation-may-be-synchronous-in-reactjs.htmIn other words, in v4, sometimes this problem manifests itself, and sometimes it doesn’t, probably depending on what triggered the initial dispatch that causes the store to update. Thanks for fixing this in v5.
If you’re interested in the lifecycle, you can explore the relevant source code
@naw Because subscribing is done in
componentWillMount
, and that method fires from bottom-up, currently in react-redux 4.x (and fixed in the 5.0 beta) children subscribe to the redux store before their parents. This means, when store state changes, the children get notified first and rerender themselves. Then the parent component gets notified. This can lead to extra renders of the children where they have stale props because their parent hasn’t been updated yet. The fix was to make children subscribe to their parents, and only toplevel connected components subscribe directly to the store. This ensures subscriptions always fire top to bottom.Every component that connected to the store like this would setState on every store state change, which would destroy performance. It would also run into issues with child components rerendering before parents, which means its state may be out of sync with its props.
As far as I can tell, the original example is broken because the reducer is trying to A) use a promise, and B) do a return inside that promise. Thus, returning
undefined
.Also, conceptually what
render()
does isn’t “binding”, so it’s probably best to avoid that terminology.@cubiccompass : Those are separate things.
getInitialState
is the method used to initialize local component state with theReact.createClass()
component definition syntax.mapStateToProps
is the method that React-Redux’sconnect
function uses to extract pieces of data from the store and pass them to a component as props. Those are not mutually exclusive.You can have a component that renders data using props retrieved from the Redux store, props passed down by a parent component, and local component state, all at once. How you write your own render functions is up to you. Example: