DEV Community

Origami
Origami

Posted on

そのContext呼び出しはProvider以下にあると保証できますか?

はい

reactのcontextは便利です。Reduxキラーとして君臨し、多くの開発者を苦しめてきました。特にhooksが登場してからというもの、useContext()によりその利便性はさらに高くなりました。

const {data} = useContext(DataContext);

あるいは

<DataContext.Consumer>
    {({data}) => /* render something */}
</DataContext.Consumer>

さて、このときDataContext.Provider以下でこれらが呼び出されることを保証できるでしょうか?結論から言うとできません。TypeScriptにおいても同様です(そりゃそうだ)
ちなみに、ProviderがなければcreateContextのデフォルト値がそのまま利用できます。

const DataContext = createContext({ hai: "haijanaiga" } as
    {
        hai: string
    });

export default App: React.FC = () => {
  const data = useContext(DataContext);
  return <h1>{data.hai}</h1>;
}; // its equally `<h1>haijanaiga</h1>`

しかし、たいていの場合において<Context.Consumer />およびuseContext()はProvider以下で呼びたいことが殆どでしょう。
しかし、Reactはこのときエラーになってくれません。デフォルト値が存在するためです。
また、ほとんどの場合開発時にのみ問題になりうるものですから、そのときにエラーになってくれればよいものです。すると、次のようなコードを書くことになるでしょう…

type SomeContextType = {
    hai: string
}

const DataContext = createContext(undefined as undefined | SomeContextType);

const DataProvider = DataContext.Provider;

const useDataCtx = (): SomeContextType | never => {
    const ctx = useContext(DataContext);
    if (!ctx) throw new Error("Not children of DataContext.Provider")
    return ctx;
}

このとき、<DataProvider state={{hai: ""}}>がない状態でuseDataCtx()が呼び出されても、適正にエラーが送出されるため、健全性が高まると言えます。
あるいは、非同期処理を利用などの理由でわざとundefinedを挿入しているとしても、if (typeof ctx === "undefined")などで代替し、読み込みの状態についてはnullを注入することもできます。もっとも、nullを注入するような行為は健全性に欠けますが…
単純に考えましょう。読み込み中であるstateたとえば{suspending: true}をProviderに注入する方法が効きます。

ところであなたのContextは健全ですか?

Top comments (0)