diff --git a/components/x-interaction/__tests__/x-interaction.test.jsx b/components/x-interaction/__tests__/x-interaction.test.jsx index e021090d3..2708f39ad 100644 --- a/components/x-interaction/__tests__/x-interaction.test.jsx +++ b/components/x-interaction/__tests__/x-interaction.test.jsx @@ -99,6 +99,23 @@ describe('x-interaction', () => { ).toBe(10); }); + it('should update props of base using async updater function from action', async () => { + const Base = () => null; + const Wrapped = withActions({ + foo: () => async ({bar}) => ({ bar: bar + 5 }), + })(Base); + + const target = mount(); + + await target.find(Base).prop('actions').foo(); + target.update(); + + expect( + target.find(Base).prop('bar') + ).toBe(10); + }); + + it('should wait for promises and apply resolved state updates', async () => { const Base = () => null; const Wrapped = withActions({ diff --git a/components/x-interaction/src/InteractionClass.jsx b/components/x-interaction/src/InteractionClass.jsx index 50e4c04b7..a5288fd93 100644 --- a/components/x-interaction/src/InteractionClass.jsx +++ b/components/x-interaction/src/InteractionClass.jsx @@ -11,7 +11,7 @@ export class InteractionClass extends Component { inFlight: 0, }; - this.actions = mapValues(props.actions, (func) => (...args) => { + this.actions = mapValues(props.actions, (func) => async (...args) => { // mark as loading one microtask later. if the action is synchronous then // setting loading back to false will happen in the same microtask and no // additional render will be scheduled. @@ -19,24 +19,24 @@ export class InteractionClass extends Component { this.setState(({ inFlight }) => ({ inFlight: inFlight + 1 })); }); - return Promise.resolve(func(...args)).then((next) => { - const updater = typeof next === 'function' - ? ({state}) => ({state: Object.assign( - state, - next(Object.assign( - {}, - props.initialState, - state - )) - )}) - : ({state}) => ({state: Object.assign(state, next)}); - - return new Promise(resolve => - this.setState(updater, () => ( - this.setState(({ inFlight }) => ({ inFlight: inFlight - 1 }), resolve) - )) - ); - }); + const stateUpdate = await Promise.resolve(func(...args)); + + const nextState = typeof stateUpdate === 'function' + ? Object.assign( + this.state.state, + await Promise.resolve(stateUpdate(Object.assign( + {}, + props.initialState, + this.state.state + ))) + ) + : Object.assign(this.state.state, stateUpdate); + + return new Promise(resolve => + this.setState({state: nextState}, () => ( + this.setState(({ inFlight }) => ({ inFlight: inFlight - 1 }), resolve) + )) + ); }); }