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
【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
ここまでで、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
【./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
■動作確認
↓ リンククリックで「クライアントナビゲーション」してページ遷移する
もっと応援したいなと思っていただけた場合、よろしければサポートをおねがいします。いただいたサポートは活動費に使わせていただきます。