I have a little problem in my React/Redux application.
When I dispatch an action to change my redux state, React component receives the changed state asynchronously. But I want to handle the changes synchronously.
The code is here:
// Component
class App extends React.Component {
handleButtonChange = e => {
this.props.change("Hello World");
console.log(this.props.title); // print old value
setTimeout(() => {
console.log(this.props.title); // print new value
}, 100);
};
render() {
return (
<div className="App">
<button onClick={this.handleButtonChange}>
Change the title
</button>
</div>
);
}
}
const mapStateToProps = state => ({
title: state.title
});
const mapDispatchToProps = dispatch => ({
change: title => dispatch(change(title))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
Any ideas?
The full example is here: https://codesandbox.io/s/lxjkvvk3pl
The connect()
method, it does not modify the component class passed to it; instead, it returns a new, connected component class for you to use.
The mapStateToProps()
function is called any time the connector component needs to compute new props, as a result of a store state change.
Reference: react-redux api
When you dispatch an action:
this.props.change("Hello World");
console.log(this.props.title);
Which will make the store state change, as a result, the mapStateToProps()
function will be invoked and the connect()
function will get the current component and return for you a new component with the updated props
.
The console.log(this.props.title)
is executed with the value of the property title of the old props object of the old component.
I put some lifecycle hooks in your codes to see what actually happened:
import React from "react";
import { connect } from "react-redux";
import { change } from "./actions";
class App extends React.Component {
constructor(props) {
super(props);
console.log("contructor(): " + this.props.title);
}
handleButtonChange = e => {
this.props.change("Hello World");
console.log("handleButtonChange(): " + this.props.title);
};
componentDidMount() {
console.log("componentDidMount(): " + this.props.title);
}
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.title !== prevProps.title) {
console.log("componentDidUpdate(): " + this.props.title);
}
}
componentWillUnmount() {
console.log("componentWillUnmount(): " + this.props.title);
}
render() {
console.log("render(): " + this.props.title);
return (
<div className="App">
<button onClick={this.handleButtonChange}>Change the title</button>
</div>
);
}
}
const mapStateToProps = state => ({
title: state.title
});
const mapDispatchToProps = dispatch => ({
change: title => dispatch(change(title))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
And the results after clicking four times on the button are:
Therefore, in case you want to use the new computed props for the first time, it's updated, you should do it in the componentDidUpdate()
However, I also see that you want to use the new props
directly after dispatching an action:
this.props.change("Hello World");
// stop here, creating a new component, merging props, then executing the next line
console.log(this.props.title);
I think that's somehow impossible.
The rework demo: https://codesandbox.io/s/wol55qqx0k