react入門5
おさらい
入門1:
Webページを作る際、JSXと関数コンポーネントでES6よりシンプルにファイル分割する方法
関数コンポーネントから別の関数コンポーネントを呼び出し、呼び出したコンポーネントにリードオンリーの引数propsを渡す方法
入門2:
関数コンポーネントの内部に状態を保持する変数stateおよびstateを更新する関数を定義し、利用する方法(useState)
reactでbuttonなどの要素のイベントを扱う方法
入門3:
動的にJSX(HTML要素および自分で定義した他の関数コンポーネント)を呼び出す方法={JSXが入った変数(配列)}
JSXのループによる複数記述、ifによる記述選択
JSXの動的削除=再レンタリングのタイミング
入門4:
親コンポーネントと子コンポーネントの関係
小コンポーネントから親コンポーネントのstateを更新する方法
親を介して兄妹へ情報を渡す例
グローバル変数?設定ファイル?:useContext
他の言語でソフトウェアを開発している人ならどのモジュール、コンポーネントからも自由にアクセスできる悪名高いグローバル変数は使わないようにしていると思います。ただ、どうしても、本筋のデータ構造とは関係ない、複数のコンポーネントに設定したい項目があると思います。例えば表示においてダークモードにする際には、ボタンや背景などすべての表示に関するコンポーネントに設定値を伝えるなど。この時に、react親から子へのpropsでの本筋のコンテンツのデータフローに、別の文脈の表示設定などが入り込むと、可読性が下がります。そんな時に、useContextが使われます。
上記のような構造は下記のようなコード記載できます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>test</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
//index.js
import React, { StrictMode } from 'react'
import ReactDOM from 'react-dom/client'
import P from './P'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<StrictMode>
<P/>
</StrictMode>
)
トップのコンポーネントとしてPを呼び出しています。
//P.js
import ChA1 from "./ChA"
import ChB1 from "./ChB"
const P = ()=>
{
return(
<>
<ChA1 number={1}/>
<ChB1 message={"あああ,いいい"}/>
</>
)
}
export default P
Pは、ChA1、ChB1モジュールを呼び出しています。最初の図のように引数を渡しています。
//ChA.js
import "./global.css"
const ChA1 = (props)=>{
return(
<ChA2 number={props.number+1}/>
)
}
const ChA2 = (props)=>{
return(
<>
<h2 className={"white"}>ChA2:{props.number}</h2>
<ChA3 number={props.number +2}/>
</>
)
}
const ChA3 = (props)=>{
return(
<h3 className={"white"}>ChA3:{props.number}</h3>
)
}
export default ChA1
ChA1はChA2を、ChA2はChA3を呼び出しています。上位から受け取ったpropにChA1は1を、ChA2は2を足しています。
CCSを使用しています。reactでは、要素中にclassName={”クラス名”}でCSSファイルに設定したクラスのスタイルを適用できます。
//ChB.js
import "./global.css"
const ChB1 = (props)=>{
const massages = props.message.split(',')
return(
<>
<ChB11 message={massages[0]}/>
<ChB12 message={massages[1]}/>
</>
)
}
const ChB11 = (props)=>{
return(
<h2 className={"white"}>ChB11 {props.message}</h2>
)
}
const ChB12 = (props)=>{
return(
<h3 className={"white"}>ChB12 {props.message}</h3>
)
}
export default ChB1
ChB1は、ChB11とChB12を並列に呼び出しています。ただし親から受け取った引数をカンマ区切りで分解して渡しています。
/*global.css*/
.black{
background-color:rgb(0,0,0);
color: rgb(240, 240, 240);
}
.white{
background-color:rgb(240,240,240);
color: rgb(10, 10, 10);
}
CSSは一般の書き方と同じです。グローバルに使用されます。
表示を変えたい
さて、今、ダークモードにしたいという要求が出ました。表示モジュールはCh A側にも、ChB側にも存在しています。親コンポーネントでpropを伝達することはあまり適切ではない気がします。
こんなとき、多くのモジュールをある文脈(今回は表示)で横断的に設定できる変数useContextの出番です。以下の図で点線に相当します。
次のような構造に改造します。
まずは、モードセレクトをするコンポーネントを作成します。親から渡されたprops.setModeで親に選択されたモードを渡します。
//Modejs
const Mode = (props)=>{
const handle_select = (e)=>{
props.setMode(e.target.value)
}
return(
<>
<select onChange={handle_select}>
<option value="white">白</option>
<option value="black">黒</option>
</select>
</>
)
}
export default Mode
親コンポーネントは、Modeコンポーネントを使って"white","black"どちらかの文字列をuseStateで定義したmode変数に格納してもらいます。
//P.js
import { useState,createContext } from "react"
import ChA1 from "./ChA"
import ChB1 from "./ChB"
import Mode from "./Mode"
//グローバルな空間にcreateContextのオブジェクトを生成する。
//他のファイルで使うのでexportをつけること。
export const modeContext = createContext()
const P = ()=>
{
const [mode,setMode] = useState("white")
return(
<>
<Mode setMode={setMode}/>
<modeContext.Provider value={mode}>
<ChA1 number={1}/>
<ChB1 message={"あああ,いいい"}/>
</modeContext.Provider>
</>
)
}
export default P
この"white","black"どちらかの文字列modeをChA2,ChA3,ChB11,ChB12のclassNameに設定したいです。そこで、コンポーネント外にcreateContext()のオブジェクトmodeContextを生成し、他のファイルからもこのmodeContextオブジェクトにアクセスできるようにします。
Pコンポーネント内部では、このオブジェクトをJSXとして使い、どのコンポーネントの範囲に情報を公開するか、何を渡すかを設定します。
<createContextのインスタンス.Provider value={渡すオブジェクト}>
情報を渡したいコンポーネントあるいはその上位コンポーネント
</createContextのインスタンス.Provider>
ChA2,ChA3はChA1に、ChB11,ChB12はChB1に属しているので、これらをProvider タグで括ります。valueには、state変数も渡せます。(state変数は実行の瞬間はただのconst変数です)
/ChA.js
import { useContext } from "react"
import { modeContext } from "./P"
import "./global.css"
const ChA1 = (props)=>{
return(
<ChA2 number={props.number+1}/>
)
}
const ChA2 = (props)=>{
const mode = useContext(modeContext)
return(
<>
<h2 className={mode}>ChA2:{props.number}</h2>
<ChA3 number={props.number +2}/>
</>
)
}
const ChA3 = (props)=>{
const mode = useContext(modeContext)
return(
<h3 className={mode}>ChA3:{props.number}</h3>
)
}
export default ChA1
//ChB.js
import "./global.css"
import { useContext } from "react"
import { modeContext } from "./P"
const ChB1 = (props)=>{
const massages = props.message.split(',')
return(
<>
<ChB11 message={massages[0]}/>
<ChB12 message={massages[1]}/>
</>
)
}
const ChB11 = (props)=>{
const mode = useContext(modeContext)
return(
<h2 className={mode}>ChB11 {props.message}</h2>
)
}
const ChB12 = (props)=>{
const mode = useContext(modeContext)
return(
<h3 className={mode}>ChB12 {props.message}</h3>
)
}
export default ChB1
contextを受け取る側は、import { useContext } from "react"の他、先ほどPコンポーネントでエクスポートしたcreateContextのインスタンスをインポートします。
あとは、利用するコンポーネントの中(ただしいつもの通りレンダリングを扱うreturnの前)で変数に格納し、JSXの中で利用します。
この記事が気に入ったらサポートをしてみませんか?