I think you are misunderstanding the data flows through redux. I assume when you want to turn the action into a promise, you would want that promise to resolve when the redux state has been updated - this appears to be the case from your other question. This is not generally how redux is designed to work.
When you dispatch an action, the store will be updated, and then the props in your components that come from the store will update. All you need to do is use those props in your components, and whenever the props update, so will your component.
In your initial question your code was:
onSubmit(e) {
e.preventDefault();
this.props.nextSentence(); //this is async
this.setState({ //this is not yet updating with the right values then
inputField: this.props.activePupil.name
});
}
The issue you have is that activePupil
is part of your redux store, yet you are trying to store it within the state of your component as well. This is a bad idea because you no longer have a single source of truth for your state.
The better approach is to just use this.props.activePupil
wherever it is needed, e.g.
render() {
return (
<span>{this.props.activePupil.name}</span>
);
}
Now when you dispatch the action, as soon store is updated, your component will also update. If you need to then you can add a loading
property to your store, so that you can indicate to the user that something is happening, and dispatch this from your thunk before doing any async work, and then revert loading
when UPDATE_ACTIVE
is dispatched.
export const nextPupil = () => {
return (dispatch, getState) => {
dispatch({ type: "PUPIL_UPDATING });
const {activeSentence, sentencesList} = getState();
//... some more code
const nextActive = pupilList[nextIndex];
dispatch({ type: "UPDATE_ACTIVE", payload: nextActive });
}
};
Edit 1
It's taken a bit of thinking about the right approach to your issue below, but I think I have settled on a reasonable way to achieve it, while maintaining data integrity. It does involve keeping form input in your store, which not everyone is a fan of, but is not a problem for me.
Anyway... we need another reducer for handling user input - it will be very simple and respond to two actions - the "UPDATE_ACTIVE" action you already have, and an "NAME_UPDATED" action:
const updatedName = (state = '', action) => {
switch (action.type) {
case 'UPDATE_ACTIVE':
return action.payload.name;
case: 'NAME_UPDATED':
return action.payload.updatedName;
default:
return state;
}
};
Then we will need a simple action creator:
const inputChanged = updatedName => ({
type: 'NAME_UPDATED',
payload: { updatedName }
});
Now within our component we will use the newly created state and action creator to manage our input (just connect
them in as per usual) - lots of code missing here but there should be enough to get the idea.
class Whatever extends React.Component {
constructor() {
super();
this.handleChange= this.handleChange.bind(this);
}
handleChange(event) {
this.props.inputChanged(event.target.value);
}
render() {
return (
<input type="text" value={this.props.updatedName} onChange={this.handleChange} />
);
}
}
I hope that makes sense!
Edit 2:
If you want to store the input on component state rather than within the redux store, then I think this approach should work. It uses the componentWillReceiveProps lifecycle hook - to set the component state i.e. the input value when the activePupil
prop changes. This is called every time the props update, but may also be called when there aren't any prop changes, so we need some logic to decide whether to update the updatedPupilName
prop. To achieve this we are comparing our current props.activePupil.name
to the nextProps.activePupil.name
and only updating the state if they are different. If you have an ID type property on your pupils this would probably be better suited for the comparison.
class Whatever extends React.Component {
constructor(props) {
super(props);
this.state = {
updatedPupilName: props.activePupil.name
};
this.handleChange= this.handleChange.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.activePupil.name !== this.props.activePupil.name) {
this.setState({
updatedPupilName: nextProps.activePupil.name
});
}
}
handleChange(event) {
this.setState({
updatedPupilName: event.target.value
});
}
render() {
return (
<input type="text" value={this.state.updatedPupilName} onChange={this.handleChange} />
);
}
}