1 | ;
|
2 |
|
3 | exports.__esModule = true;
|
4 | exports.default = useStateAsync;
|
5 | var _react = require("react");
|
6 | /**
|
7 | * A hook that mirrors `useState` in function and API, expect that setState
|
8 | * calls return a promise that resolves after the state has been set (in an effect).
|
9 | *
|
10 | * This is _similar_ to the second callback in classy setState calls, but fires later.
|
11 | *
|
12 | * ```ts
|
13 | * const [counter, setState] = useStateAsync(1);
|
14 | *
|
15 | * const handleIncrement = async () => {
|
16 | * await setState(2);
|
17 | * doWorkRequiringCurrentState()
|
18 | * }
|
19 | * ```
|
20 | *
|
21 | * @param initialState initialize with some state value same as `useState`
|
22 | */
|
23 | function useStateAsync(initialState) {
|
24 | const [state, setState] = (0, _react.useState)(initialState);
|
25 | const resolvers = (0, _react.useRef)([]);
|
26 | (0, _react.useEffect)(() => {
|
27 | resolvers.current.forEach(resolve => resolve(state));
|
28 | resolvers.current.length = 0;
|
29 | }, [state]);
|
30 | const setStateAsync = (0, _react.useCallback)(update => {
|
31 | return new Promise((resolve, reject) => {
|
32 | setState(prevState => {
|
33 | try {
|
34 | let nextState;
|
35 | // ugly instanceof for typescript
|
36 | if (update instanceof Function) {
|
37 | nextState = update(prevState);
|
38 | } else {
|
39 | nextState = update;
|
40 | }
|
41 |
|
42 | // If state does not change, we must resolve the promise because
|
43 | // react won't re-render and effect will not resolve. If there are already
|
44 | // resolvers queued, then it should be safe to assume an update will happen
|
45 | if (!resolvers.current.length && Object.is(nextState, prevState)) {
|
46 | resolve(nextState);
|
47 | } else {
|
48 | resolvers.current.push(resolve);
|
49 | }
|
50 | return nextState;
|
51 | } catch (e) {
|
52 | reject(e);
|
53 | throw e;
|
54 | }
|
55 | });
|
56 | });
|
57 | }, [setState]);
|
58 | return [state, setStateAsync];
|
59 | } |
\ | No newline at end of file |