見出し画像

nextjs with typescript:37 useContext

※今回はNext.jsではなくReact フックの話。実行環境としてNext.jsを利用している。

【1】useContext

要するに、これを使うとグローバルなStateを取り扱うことができる、ということ。(※Reduxを導入せず、React単体で取り扱う手段の場合はこれをつかう)

【2】使い方

(1)まず、「createContext()」で「グローバルなstate(変数)」を取り扱うコンテキストを作成する。

【./UserContext.ts】

import {createContext} from 'react';

export const MyUserContext = createContext<string>("");

(2)プロバイダコンポーネントを使い、 「value属性」にグローバルなstate値を設定する
 例えばNext.jsの場合は、_app.tsxを用意してプロバイダコンポーネントでメインコンポーネントをラッピングする。

【./pages/_app.tsx】※material-UIの説明時のサンプルをコピーしてちょっと修正。
グローバルなstateとして「文字列:hello from context!!」を設定している。

import React from 'react';
import Head from 'next/head';
import { AppProps } from 'next/app';
import { MyUserContext } from '../UserContext';


export default function MyApp(props: AppProps) {
 const { Component, pageProps } = props;

 React.useEffect(() => {
   // Remove the server-side injected CSS.
   const jssStyles = document.querySelector('#jss-server-side');
   if (jssStyles) {
     jssStyles.parentElement!.removeChild(jssStyles);
   }
 }, []);

 return (
   <React.Fragment>
     <Head>
       <title>My page</title>
       <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
     </Head>

       {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
       <MyUserContext.Provider value="hello from context!!">
           <Component {...pageProps} />
       </MyUserContext.Provider>
   </React.Fragment>
 );
}

(3)useContextでstateをとって表示する
【./pages/usecontextdemo1.tsx】

import {useContext} from 'react';

import { MyUserContext } from "../UserContext";

const UseContextDemo1 = () =>{

   const msg = useContext(MyUserContext);

   return(
       <div>
           {msg}
       </div>
   );
}

export default UseContextDemo1

画像1

【3】複数のグローバルなstateを用意する場合

複数のstateを用意する場合は、その分だけcontextを作る。プロバイダコンポーネントのラップもその数だけおこなう。

■例:グローバルなstateを2つもたせる

【./UserContext.ts】

import {createContext} from 'react';


export const MyNumberContext = createContext<number>(0);
export const MyUserContext = createContext<string>("");

【./pages/_app.tsx】
※複数のグローバルなstateを設定するには、プロバイダコンポーネントを入れ子にしていく。

import React from 'react';
import Head from 'next/head';
import { AppProps } from 'next/app';
import { MyNumberContext, MyUserContext} from '../UserContext';


export default function MyApp(props: AppProps) {
 const { Component, pageProps } = props;

 React.useEffect(() => {
   // Remove the server-side injected CSS.
   const jssStyles = document.querySelector('#jss-server-side');
   if (jssStyles) {
     jssStyles.parentElement!.removeChild(jssStyles);
   }
 }, []);



 return (
   <React.Fragment>
     <Head>
       <title>My page</title>
       <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
     </Head>

       {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
       <UserContext.Provider value={{value, setValue}}>
         <MyUserContext.Provider value="hello from context!!">
           <MyNumberContext.Provider value={777}>
             <Component {...pageProps} />
           </MyNumberContext.Provider>
         </MyUserContext.Provider>
       </UserContext.Provider>
   </React.Fragment>
 );
}

【./pages/usecontextdemo1.tsx】

import {useContext} from 'react';

import { MyNumberContext, MyUserContext } from "../UserContext";

const UseContextDemo1 = () =>{

   const msg = useContext(MyUserContext);
   const yournum = useContext(MyNumberContext);

   return(
       <div>
           {msg} your lucky number is  {yournum}
       </div>
   );
}

export default UseContextDemo1

画像2

 ここまでで、useContextを使うことでグローバルなstateの値を取得することはできる。ただし、stateを操作するフック(関数)がないためこのままではstate変更ができない。

【4】グローバルなstateを変更する

グローバルなstateを変更するにはcreateContextを作成する際に「state」と「stateを操作する関数(hook)」を渡しておく。

■例:useStateと組み合わせる
【./UserContext.ts】
※typescript向けに型定義と初期値も用意

import {createContext} from 'react';


export interface MyContextType{
   value:string;
   setValue:(value:string)=>void;
}

//初期値
const initialMyContext:MyContextType={
   value:"",
   setValue:()=>{}
}

export const UserContext = createContext<MyContextType>(initialMyContext);

【./pages/_app.tsx】
プロバイダコンポーネントに値を渡すときに、「useState()」で「state」と「フック」を用意して、その「state」と「フック(関数)」をvalue属性に渡す。

import React from 'react';
import Head from 'next/head';
import { AppProps } from 'next/app';
import { UserContext } from '../UserContext';

import {useState} from 'react';


export default function MyApp(props: AppProps) {
 const { Component, pageProps } = props;

 React.useEffect(() => {
   // Remove the server-side injected CSS.
   const jssStyles = document.querySelector('#jss-server-side');
   if (jssStyles) {
     jssStyles.parentElement!.removeChild(jssStyles);
   }
 }, []);


 //useContextに渡すstate
 const [value,setValue] = useState<string>("hello from context with useState");


 return (
   <React.Fragment>
     <Head>
       <title>My page</title>
       <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
     </Head>

       {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
       <UserContext.Provider value={{value, setValue}}>
           <Component {...pageProps} />
       </UserContext.Provider>
   </React.Fragment>
 );
}

・画面:※今回は2つの画面を用意
useContextの返却値は[]ではなく{ }で囲むことに注意する

【./pages/usecontextdemopage1.tsx】

import { useContext } from "react";
import { UserContext } from "../UserContext";
import Link from 'next/link';

const UseContextDemoPage1 = () =>{
   
   //受け付ける引数は[]ではなく、{ }で囲む
   const {value, setValue} = useContext(UserContext);

   return (
       <div>
           {value}
           <hr/>
           <Link href="/usecontextdemopage2">
               <a>usercontextdemopage2</a>
           </Link>
       </div>
   );
}

export default UseContextDemoPage1

画像3

【./pages/usecontextdemopage2.tsx】

import { useContext } from "react";
import { UserContext } from "../UserContext";
import Link from 'next/link';

const UseContextDemoPage2 = () =>{
   
   //受け付ける引数は[]ではなく、{ }で囲む
   const {value, setValue} = useContext(UserContext);

   return (
       <div>
           {value}
           <button value={value} onClick={()=>setValue("hello!!!")}>change</button>
           <hr/>
           <Link href="/usecontextdemopage1">
               <a>usercontextdemo</a>
           </Link>
       </div>
   );
}

export default UseContextDemoPage2

画像4

■動作確認

画像5

↓ リンククリックで「クライアントナビゲーション」してページ遷移する

画像6


もっと応援したいなと思っていただけた場合、よろしければサポートをおねがいします。いただいたサポートは活動費に使わせていただきます。