export function AsyncNode({ children }: { children: Promise }) { return useLatestResolvedValue(children, () => null) as any; // as any assertion needed for issue https://github.com/DefinitelyTyped/DefinitelyTyped/issues/20356#issuecomment-336384210 } export function useLatestResolvedValue( promise: Promise, initial: () => Value ) { const [[value], dispatch] = useReducer< Reducer< [Value, Array>], | { type: "enqueue"; promise: Promise } | { type: "resolved"; promise: Promise; value: Value } > >(latestResolvedPromiseReducer, [initial(), []]); useEffect(() => { dispatch({ type: "enqueue", promise }); promise.then(node => dispatch({ type: "resolved", value: node, promise })); }, [promise]); return value; } function latestResolvedPromiseReducer( [display, queue]: [Value, Array>], action: | { type: "enqueue"; promise: Promise } | { type: "resolved"; promise: Promise; value: Value } ): [Value, Array>] { 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 = {Promise.resolve(

hi

)}
; const example2 = ; 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 (
setText(e.target.value)} />
{response}
{content}
); }