Closed7

[React / TypeScript / Vite] useContextを理解したい

あざらしあざらし

おさらい

  • フックは関数の中の一番外で呼び出す
  • もちろんカスタムフックを作ったならば、カスタムフックも関数の中の一番外で呼び出す
あざらしあざらし

固定値など変化しない値を送りたいとき

ベーシックな使い方
単純にコンテキストを作って(createContext)、受けたい人が勝手に受けておく(useContext)だけ。

親.tsx

import Kodomo from "子"
export const ContextTest = createContext("a");  // ここで送りたいデータを作成

export default function Oya() {
~略~

 return(
   <Kodomo />
 );
}

子.tsx(なんもしない)

import Mago from "孫"

export default function Kodomo() {
~略~

 return(
   <Mago />
 );
}

孫.tsx

import ContextTest from "親"

export default function Mago() {
  const ababa = useContext(ContextTest);  // ここでデータを受けとる

~略~

 return(
   <div>{ababa}</div>
 );
}
あざらしあざらし

useStateをつかって可変な値を送りたいとき

親の送り方だけが変わる
子や孫はベーシックなものと同じ方法で問題ない

送りたいデータ.tsx

import { useEffect, useState } from "react";

export default function useOkuru() {
  const [send, setSend] = useState("");

  const makeSend = () => {
    useEffect(() => {
      // なんか更新する処理
      const tekitou = "a";
      setSend(tekitou);
    }, []);
  }

  return { send, makeSend };
}

みたいなかんじのデータ send を送りたいとする

親.tsx

import { createContext } from "react";
import useOkuru from "送りたいデータ";
import Kodomo from "子"

// ここでまずコンテキストを作っておく
export const ContextTest = createContext<型 | undefined>(undefined);   

export default function Oya() {
  { send, makeSend } = useOkuru();
  makeSend();

  return(
    <ContextTest.Provider value={send}>      // Providerで囲む。送りたいデータはvalue={}
      <Kodomo />
    </ContextTest.Provider>
  );
}
あざらしあざらし

はまったこと

下記エラーがでた

Uncaught Error: Module "vm" has been externalized for browser compatibility. Cannot access "vm.createContext" in client code.  See https://vitejs.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.
    at Object.get (__vite-browser-external:vm:3:11)

これはViteのエラーで、「vm.createContext」なんて入れてねえから使えねえよ!!!!!と怒られています。
vm モジュールはNode.jsのモジュールなので、




ん???createContextってReactからimportするんだよね…?あれ???
と思って、import 欄を見てみたら、

import { createContext } from "react";

ではなくて

import { createContext } from "vm";

が入ってました。そりゃ怒られる。VSCodeの予測変換も一番上が正しいとは限らないから気をつけようね(自戒)

あざらしあざらし

データの更新もしたいばあい

useStateで、任意のタイミングでsetもしていく場合、更新する関数も一緒にProviderで渡す必要がある

(関数渡さずに値だけ渡すと、確かに更新関数は走るんだけど親目線値が更新されないw(すごく悩んだ))

https://react.keicode.com/basics/context-usestate-hook-update.php

送りたいデータ.tsx

import { useEffect, useState } from "react";

export default function useOkuru() {
  const [send, setSend] = useState("");

  const makeSend = (tekitou: string) => {
      setSend(tekitou);
  }

  return { send, makeSend };
}

親.tsx

import { createContext } from "react";
import useOkuru from "送りたいデータ";
import Kodomo from "子"

// ここでまずコンテキストを作っておく
export const ContextTest = createContext<型 | undefined>(undefined);   

export default function Oya() {
  { send, makeSend } = useOkuru();

  return(
    <ContextTest.Provider value={{ send, makeSend }}>      // makeSend関数もvalueにぶちこむ
      <Kodomo />
    </ContextTest.Provider>
  );
}

makeSendを使いたい場合も、useContextで拾う

import ContextTest from "親"

export default function Mago() {
  const makeSend = useContext(ContextTest).makeSend ; 

~略~

 return(
   <なんかボタンとか onClick={() => makeSend("kousin")} />
 );
}
あざらしあざらし

Contextは本当に全画面で使いそうなもの以外はいれないほうがよさそう

  • スコープでかい
  • いわゆる超グローバル変数みたいなもの
  • つまり乱用するとあぶない
  • グローバル変数たくさんあるとつらい

用法容量まもろうね

このスクラップは2ヶ月前にクローズされました