UNPKG

1.88 kBJavaScriptView Raw
1"use strict";
2
3exports.__esModule = true;
4exports.default = useStateAsync;
5var _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 */
23function 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