TypeScriptでuseContextを使う際に、createContextの型チェックにハマった件について
NextとTypeScriptで状態管理をするためにuseContext
を使用しようとして、NetlifyのuseContextの導入の記事なんかを参考に導入を進めていたのですが、記事通りに実装したら以下のエラーが発生。
Error: Element type is invalid: expected a
string (for built-in components) or
a class/function (for composite components) but got: undefined.
You likely forgot to export your component
from the file it's defined in, or you might have mixed up default
and named imports.
直訳すると、
エラーが発生しました。要素のタイプが無効です。
string(組み込みコンポーネントの場合)または
class/function(複合コンポーネントの場合)を期待しましたが、「undefined」となりました。
コンポーネントを定義されたファイルからexportするのを忘れたか
定義されているファイルからコンポーネントをexportするのを忘れたか、
デフォルトのimportと名前付きのimportを混同した可能性があります。
エラーが起きている箇所はcreateContext
の部分です。
interface Props {
children: ReactNode | ReactNode[];
}
const PostContext = React.createContext<Partial<Props>>({});
上記のようにcreateContext
を使っていたのですが、これだと問題があります。
それは、createContext
のdefaultValue
がundifined
となってしまう可能性があり、contextを使いたいComponentではundifinedかどうかを毎回チェックする必要が出てくるということです。ちなみにチェックせずにundifined
をそのまま渡してもどこかでエラーになってしまいます。参考(React公式)
解決策
さて、ここからはこの問題、つまりcreateContextのdefaultValueがundifinedのままuseContextで使用されてしまうこと
の解決策を説明します。
簡単にいうと
解決策を言葉で説明するなら、undifinedのdefaultValueがcontextで各コンポーネントに渡らないようにフィルタリングしてあげる
ということです。
実装しよう
今回は以下の記事を参考に実装しました。
React Context with TypeScript: Part 4 - Creating a context with no default and no undefined check
【React】デフォルト値もundefinedチェックもいらないcreateContext【Typescript】
React ContextのdefaultValueのちょっとかしこい(かもしれない)使い方
PostContext.tsx
import React, { useContext, useState } from "react";
type postContextType = {
getCurrentPost: () => void;
};
function createCtx<ContextType>() {
const ctx = React.createContext<ContextType | undefined>(undefined);
const useCtx = () => {
const c = React.useContext(ctx);
if (!c) throw new Error("useCtx must be inside a Provider with a value");
return c;
};
return [useCtx, ctx.Provider] as const;
}
export const [usePost, SetPostProvider] = createCtx<postContextType>();
export const PostProvider: React.FC = (props) => {
const post = usePostCtx();
return <SetPostProvider value={post}>{props.children}</SetPostProvider>;
};
export const usePostCtx: any = () => {
const displayHello = () => {
console.log("Hello!!");
};
return { getCurrentPost };
};
createCtx
ではReact.useContext(ctx)
がundifinedの場合はエラーメッセージを、そうでない場合はそのままcontextを返します。
そうすることで、useContextにundifinedのdefaultValue
が渡った場合にはエラーとなるので、undifinedに気づくことができるというわけです。
今回は、確認のためにコンソールにHello!!
と表示するdisplayHello
という関数を作ってみました。
画像の通り、Hello!!
と表示されているので、エラーは治り、無事にcontextが使えるようになりました!!
Discussion