web-dev-qa-db-ja.com

React.useMemoによる非同期呼び出し

シナリオは比較的単純です。リモートサーバーで実行される、長期にわたるオンデマンドの計算があります。結果をメモしたいと思います。リモートリソースから非同期でフェッチしている場合でも、この計算の結果をユーザーに表示するだけで、すべてのレンダリングでこれを実行したくないため、これは副作用ではありません。

問題:React.useMemoはTypeScriptのasync/awaitを直接サポートしておらず、promiseを返すようです:

//returns a promise: 
let myMemoizedResult = React.useMemo(() => myLongAsyncFunction(args), [args])
//also returns a promise:
let myMemoizedResult = React.useMemo(() => (async () => await myLongAsyncFunction(args)), [args])

非同期関数の結果を待ち、React.useMemoを使用して結果をメモする正しい方法は何ですか?私はプレーンJSで通常のプロミスを使用しましたが、このような状況ではまだプロミスと苦労しています。

私はmemoize-oneなどの他のアプローチを試しましたが、問題は、thisコンテキストがReact関数コンポーネントの動作 メモを壊す =、それがReact.useMemoを使用しようとしている理由です。

多分私はここで丸い穴に四角いペグをはめようとしている-それがそうであるなら、それも知っていると良いでしょう。とりあえず、私はおそらく自分のメモ機能をロールするつもりです。

編集:私はそれの一部はmemoize-oneで別の愚かな間違いをしていたことだと思いますが、React.memoでここの答えを知りたいと思っています。

ここにスニペットがあります-アイデアは、renderメソッドでメモされた結果を直接使用するのではなく、イベント駆動の方法で、つまりCalculateボタンのクリックで参照するものとして使用することです。

export const MyComponent: React.FC = () => {
    let [arg, setArg] = React.useState('100');
    let [result, setResult] = React.useState('Not yet calculated');

    //My hang up at the moment is that myExpensiveResultObject is 
    //Promise<T> rather than T
    let myExpensiveResultObject = React.useMemo(
        async () => await SomeLongRunningApi(arg),
        [arg]
    );

    const getResult = () => {
        setResult(myExpensiveResultObject.interestingProperty);
    }

    return (
        <div>
            <p>Get your result:</p>
            <input value={arg} onChange={e => setArg(e.target.value)}></input>
            <button onClick={getResult}>Calculate</button>
            <p>{`Result is ${result}`}</p>
        </div>);
}
2
Doug Denniston

編集:以下の私の元の答えは、呼び出しの非同期の性質により、意図しない副作用が発生するようです。代わりに、サーバーで実際の計算をメモするか、自己記述のクロージャーを使用してargが変更されていないかどうかを確認することを検討します。それ以外の場合は、以下で説明するように、useEffectのようなものを利用できます。

問題は、async関数が常に暗黙的にpromiseを返すことだと思います。これが事実であるため、結果を直接awaitして約束を解くことができます:

const getResult = async () => {
  const result = await myExpensiveResultObject;
  setResult(result.interestingProperty);
};

ここにコードサンドボックスの例 を参照してください。

この場合、ボタンのクリックでのみ設定されるいくつかの状態オブジェクトに依存するuseEffectを利用する方が良いパターンだと思いますが、useMemoのようです同様に動作するはずです。

1
cfbender