🐢
コンポーネントのpropsに渡す代わりにuseContextを使う
たくさんの子コンポーネントを作成して
同じstateを参照したい場合、
親コンポーネントに作成したstateを渡すのが
おっくうになったので代替できないのかとおもって
ためしてみた記録です
環境
nextjsのcreate-next-app@latestを実行してプロジェクトを作成
プロジェクト設定はこちら
デフォルトを指定しています
% npx create-next-app@latest
Need to install the following packages:
create-next-app@16.1.6
✔ What is your project named? … my-app
✔ Would you like to use the recommended Next.js defaults? › Yes, use recommended defaults
Using npm.
Initializing project with template: app-tw
Installing dependencies:
- next
- react
- react-dom
Installing devDependencies:
- @tailwindcss/postcss
- @types/node
- @types/react
- @types/react-dom
- eslint
- eslint-config-next
- tailwindcss
- typescript
デバック実行
npm run dev
完成画面キャプチャ
ファイル情報を複数コンポーネントで共有して、
各コンポーネントがそれぞれの情報を表示することを目標にしています

コンポーネント構成
app
├── components
│ ├── ShowModifiedDate.tsx -- ファイルの変更日を表示するコンポーネント
│ ├── ShowName.tsx -- ファイル名を表示するコンポーネント
│ └── ShowSize.tsx -- ファイルサイズを表示するコンポーネント
├── file
│ └── upload
│ └── Page.tsx -- 表示するページ
└── lib
└── StoredFileProvider.tsx -- アップロードしたファイル情報を保持するContext
ファイル情報を保持するContext
ファイル情報を保持するためにcreateContextを使います
createContextは参照のみできるものなので、この値を更新させるために
useReducerも利用します
app/lib/StoredFileProvider.tsx
import { createContext } from "react";
export function reducer(state: FileList | null, action: FileList | null) {
return action || state;
}
export const StoredFile = createContext(null as (FileList | null));
ページに適用
ページ内でファイルをアップロードした際に、ファイル情報を更新します
StoredFileコンテキストにstateを渡し、最新のファイル情報を保持します
app/file/upload/Page.tsx
'use client';
import { useReducer } from "react";
import { reducer, StoredFile } from "@/app/lib/StoredFileProvider";
export default function Page() {
const [state, dispatch] = useReducer(reducer, null);
return (
<div>
<input type="file" name="" id="" onChange={(e) => { dispatch(e.target.files); }} />
<StoredFile
value={state}
>
</StoredFile>
</div>
);
}
子コンポーネントを用意
それぞれのコンポーネントでuseContextを呼び出す
app/components/ShowModifiedDate.tsx
import { useContext } from "react"
import { StoredFile } from "../lib/StoredFileProvider"
export const ShowModifiedDate = () => {
const state = useContext(StoredFile);
// 最終更新日
const date = new Date(state?.item(0)?.lastModified || '');
// 空文字を指定している場合、`invalid Date`となるので、isNaNでない場合のみ画面表示
return (
<span style={{ color: 'blue' }}>{!isNaN(date.getTime()) && date.toLocaleDateString()}</span>
)
}
app/components/ShowName.tsx
import { useContext } from "react"
import { StoredFile } from "../lib/StoredFileProvider"
export const ShowName = () => {
const state = useContext(StoredFile);
// ファイル名
return (
<span style={{ color: 'red' }}>{state?.item(0)?.name}</span>
)
}
app/components/ShowSize.tsx
import { useContext } from "react"
import { StoredFile } from "../lib/StoredFileProvider"
export const ShowSize = () => {
const state = useContext(StoredFile);
// ファイルサイズ
return (
<span style={{ color: 'green' }}>{state?.item(0)?.size}</span>
)
}
子コンポーネントにわたす
コンポーネントにstateを渡す必要がなくなりすっきりしました
app/file/upload/Page.tsx
<div>
<input type="file" name="" id="" onChange={(e) => { dispatch(e.target.files); }} />
<StoredFile
value={{
state
}}>
<div style={{ display: 'grid' }}>
+ <ShowName />
+ <ShowSize />
+ <ShowModifiedDate />
</div>
</StoredFile>
</div>
まとめ
useStateを使わなくてもできることがわかりました
しかし
ファイル情報を保持するだけなら、
従来通りuseStateを使ったほうが処理が明確で
わかりやすい気がしました
2026/02/03 追記
本来は簡潔に書けます
app/file/upload/Page.tsx
'use client';
import { useState } from "react";
export default function Page() {
const [file, setFile] = useState<FileList | null>(null);
const date = new Date(file?.item(0)?.lastModified || '');
return (
<div>
<input type="file" name="" id="" onChange={(e) => { setFile(e.target.files); }} />
<div style={{ display: 'grid' }}>
<span style={{ color: 'red' }}>{file?.item(0)?.name}</span>
<span style={{ color: 'green' }}>{file?.item(0)?.size}</span>
<span style={{ color: 'blue' }}>{!isNaN(date.getTime()) && date.toLocaleDateString()}</span>
</div>
</div>
);
}
Discussion
失礼します。この直感は正しいと思います。
ロジックの複雑さは、この3つの要素に切り分けできるのですが、今回のようなケースの難しさはほとんど「具体的なロジック」しかないので、
「useState」と「そもそもコンポーネントを分けない」の組み合わせで十分で、しかもそうすればシンプルに片付けられるので読みやすくなると思います!
▼ことわりなく「コンポジション」を出してしまいましたが、以下の記事で解説しています