React の useContext で undefined が返ってくる
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 1
◆ デフォルト値がデフォルト値じゃなかった
◆ Provider の value を指定しないと その内側は undefined
◆ Provider の外側は createContext のデフォルト値
◆ Provider をレンダリングするコンポーネントはその Provider の外側になる
◆ Provider の value を指定しないと その内側は undefined
◆ Provider の外側は createContext のデフォルト値
◆ Provider をレンダリングするコンポーネントはその Provider の外側になる
React を使わず WebComponents で作ってると Context ぽいグローバルにどのコンポーネントからでもデータを取得できる仕組みは積極的に使ってたのですが React だと他でも使い回せるコンポーネントが作りやすかったり Context が結構面倒でほとんどつかってませんでした
ですが 一部ライブラリを入れると props を子に渡す仕組みが提供されず 「やりたいなら Context を使って」 みたいなのもあります
仕方なく Context を使ったのですが デフォルト値で苦戦しました
今回は Preact+htm じゃなくて React+JSX になってます
App と Component1 のそれぞれで useContext して TestContext のデータを取得しています
createContext で "DEFAULT" を指定したのでどっちもそれになることを期待したのですが
結果が違っていて component1 は undefined です
Context の使い方が間違ってるのかなとあれこれ調べたのに関数コンポーネントだと Consumer とかは要らずに useContext だけで良いみたいです
これにかなーり困らせられたわけですが 原因は単純に Provider に value を渡してないことでした
渡してないんだから デフォルト値の "DEFAULT" じゃないの? と思うのですがこれは Provider の外側で使った場合の値のようです
App コンポーネントでは Provider を使ってはいますが このコンポーネントがレンダリングする部分であって App 自体は含まれません
Provider の中のツリーの Component1 が Provider の対象になります
Provider に value を指定すると無事 Component1 内の TestContext の値が value で指定したものになりました
それだと 違うから設定してるのであって デフォルト値でいいなら指定不要なんだなって思うのは仕方ないと思います
createContext に設定するものを「デフォルト値」と呼んでるのですから
みんな通りそうな罠だと思うのに StackOverflow で useContext が undefined になるとかググってもそれらしい回答が見当たらなくて更に困りました
createContext では引数を指定しません
Context を使うところでその Context の値を更新できるように Context 自体に Context の更新をする関数を持たせるのなら 結局 Provider の value へ渡す値を Provider を使うコンポーネントの state へ入れて setter 関数を作って それを Context のオブジェクトに含めるという処理が入ります
Provider を使うところで value を色々変更するのなら createContext よりも Provier を使うコンポーネントで初期値を作ったほうが良いように思います
createContext のところで初期値を作るメリットは localStorage みたいな外部から初期データを読み込む場合に コンポーネント内でそれをしたくない時くらいでしょうか
コンポーネント外のグローバルなところで
としておいて Provider を作るコンポーネントで
のように createContext で作った初期値を受け取って
そのまま Provider の value にします
ですが 一部ライブラリを入れると props を子に渡す仕組みが提供されず 「やりたいなら Context を使って」 みたいなのもあります
仕方なく Context を使ったのですが デフォルト値で苦戦しました
今回は Preact+htm じゃなくて React+JSX になってます
useContext の結果が undefined になる
const TestContext = React.createContext("DEFAULT")
const Component1 = () => {
const value = useContext(TestContext)
console.log("component1", value)
return (
<div>
{value}
</div>
)
}
const App = () => {
const value = useContext(TestContext)
console.log("app", value)
return (
<TestContext.Provider>
<div>
<h1>Test Page</h1>
<Component1 />
</div>
</TestContext.Provider>
)
}
App と Component1 のそれぞれで useContext して TestContext のデータを取得しています
createContext で "DEFAULT" を指定したのでどっちもそれになることを期待したのですが
app DEFAULT
component1 undefined
結果が違っていて component1 は undefined です
Context の使い方が間違ってるのかなとあれこれ調べたのに関数コンポーネントだと Consumer とかは要らずに useContext だけで良いみたいです
これにかなーり困らせられたわけですが 原因は単純に Provider に value を渡してないことでした
渡してないんだから デフォルト値の "DEFAULT" じゃないの? と思うのですがこれは Provider の外側で使った場合の値のようです
App コンポーネントでは Provider を使ってはいますが このコンポーネントがレンダリングする部分であって App 自体は含まれません
Provider の中のツリーの Component1 が Provider の対象になります
Provider に value を指定すると無事 Component1 内の TestContext の値が value で指定したものになりました
ドキュメントの例が不親切
下の方の API までよく読めば書いてはいるのですが 上の方の例がデフォルト値と違う値を入れる場合だけなんですよねそれだと 違うから設定してるのであって デフォルト値でいいなら指定不要なんだなって思うのは仕方ないと思います
createContext に設定するものを「デフォルト値」と呼んでるのですから
みんな通りそうな罠だと思うのに StackOverflow で useContext が undefined になるとかググってもそれらしい回答が見当たらなくて更に困りました
デフォルト値を作る場所
createContext でデフォルト値を指定してもそれを使ってくれないなら Provider をレンダリングするコンポーネントのところで初期値を作れば良いように思いますcreateContext では引数を指定しません
Context を使うところでその Context の値を更新できるように Context 自体に Context の更新をする関数を持たせるのなら 結局 Provider の value へ渡す値を Provider を使うコンポーネントの state へ入れて setter 関数を作って それを Context のオブジェクトに含めるという処理が入ります
Provider を使うところで value を色々変更するのなら createContext よりも Provier を使うコンポーネントで初期値を作ったほうが良いように思います
createContext のところで初期値を作るメリットは localStorage みたいな外部から初期データを読み込む場合に コンポーネント内でそれをしたくない時くらいでしょうか
コンポーネント外のグローバルなところで
export const FooContext = React.createContext(localStorage.foo)
としておいて Provider を作るコンポーネントで
const foo = useContext(FooContext)
のように createContext で作った初期値を受け取って
return (
<FooContext.Provider value={foo}>...</FooContext.Provider>
)
そのまま Provider の value にします