Last active
August 26, 2019 09:59
-
-
Save freddi301/608471b4178780e7738462576954f8dc to your computer and use it in GitHub Desktop.
React component and hook for rendering promises
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function AsyncNode({ children }: { children: Promise<ReactNode> }) { | |
return useLatestResolvedValue<ReactNode>(children, () => null) as any; | |
// as any assertion needed for issue https://github.com/DefinitelyTyped/DefinitelyTyped/issues/20356#issuecomment-336384210 | |
} | |
export function useLatestResolvedValue<Value>( | |
promise: Promise<Value>, | |
initial: () => Value | |
) { | |
const [[value], dispatch] = useReducer< | |
Reducer< | |
[Value, Array<Promise<Value>>], | |
| { type: "enqueue"; promise: Promise<Value> } | |
| { type: "resolved"; promise: Promise<Value>; value: Value } | |
> | |
>(latestResolvedPromiseReducer, [initial(), []]); | |
useEffect(() => { | |
dispatch({ type: "enqueue", promise }); | |
promise.then(node => dispatch({ type: "resolved", value: node, promise })); | |
}, [promise]); | |
return value; | |
} | |
function latestResolvedPromiseReducer<Value>( | |
[display, queue]: [Value, Array<Promise<Value>>], | |
action: | |
| { type: "enqueue"; promise: Promise<Value> } | |
| { type: "resolved"; promise: Promise<Value>; value: Value } | |
): [Value, Array<Promise<Value>>] { | |
switch (action.type) { | |
case "enqueue": | |
return [display, queue.concat(action.promise)]; | |
case "resolved": { | |
const index = queue.indexOf(action.promise); | |
if (index === -1) return [display, queue]; | |
return [action.value, queue.slice(index)]; | |
} | |
default: | |
throw new Error(); | |
} | |
} | |
// examples | |
const example1 = <AsyncNode>{Promise.resolve(<h1>hi</h1>)}</AsyncNode>; | |
const example2 = <RandomDelay />; | |
async function callApi(text: string) { | |
await new Promise(resolve => setTimeout(resolve, Math.random() * 3000)); | |
return `response for ${text}`; | |
} | |
function RandomDelay() { | |
const [text, setText] = useState(""); | |
const response = useMemo(() => callApi(text), [text]); | |
const content = useLatestResolvedValue(response, () => null); | |
return ( | |
<div> | |
<input value={text} onChange={e => setText(e.target.value)} /><br/> | |
<AsyncNode>{response}</AsyncNode><br/> | |
{content} | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment