Reactでコンテキストを活用したカスタムテーマカラー設定の完全ガイド

Reactでアプリケーション全体に統一されたテーマカラーを簡単に適用し、動的に切り替える機能は、現代のUI設計において重要な役割を果たします。特に、ダークモードやブランドカラーのカスタマイズが一般的となる中、効率的にテーマカラーを管理する方法を理解することは、開発者にとって大きな強みとなります。本記事では、ReactのコンテキストAPIを活用し、シンプルかつ効果的にカスタムテーマカラーを設定する方法を解説します。さらに、テーマ切り替えやユーザー設定の保存など、実用的な実装例を通じてその利便性を探ります。これにより、Reactプロジェクトでのテーマ管理を簡素化し、ユーザー体験の向上を目指すことが可能です。

目次

ReactのコンテキストAPIとは

ReactのコンテキストAPIは、コンポーネントツリー全体にデータを効率よく渡すための仕組みを提供する機能です。これにより、親コンポーネントから子孫コンポーネントにデータを渡す際に、プロップスを何層にもわたって受け渡す「プロップスドリリング」を回避できます。

コンテキストAPIの基本構造

コンテキストAPIを使用する際には、以下の3つのステップが基本です。

  1. コンテキストの作成React.createContext()で新しいコンテキストを作成します。
  2. プロバイダーの定義<Context.Provider>を使用して、コンポーネントツリーにデータを提供します。
  3. コンシューマーの使用useContext()フックや<Context.Consumer>を使用してデータを取得します。

コンテキストAPIの利用例

例えば、アプリケーション全体でユーザー情報やテーマカラーなどを管理したい場合に、コンテキストAPIが有効です。以下はテーマカラーをコンテキストで管理する例です。

import React, { createContext, useState, useContext } from 'react';

const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

function App() {
  const { theme, setTheme } = useContext(ThemeContext);

  return (
    <div style={{ backgroundColor: theme === 'light' ? '#fff' : '#333' }}>
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
    </div>
  );
}

コンテキストAPIを利用する利点

  • コードの簡素化:複数の階層をまたぐデータの受け渡しをシンプルにします。
  • グローバルな状態管理:テーマや認証情報など、アプリ全体で共有される状態の管理が容易になります。
  • 柔軟性useContext()を使用することで、どのコンポーネントからでもデータを簡単に取得できます。

コンテキストAPIは、Reactアプリケーションでのデータの管理と共有をスムーズにし、開発効率を向上させる強力なツールです。

コンテキストを活用したテーマカラー管理の利点

Reactアプリケーションにおいて、コンテキストAPIを活用したテーマカラー管理は、開発の効率化やコードの可読性向上に寄与します。特に、動的なUIテーマを必要とするプロジェクトで、その恩恵は顕著です。

グローバルなテーマ管理

コンテキストAPIを利用すると、テーマカラーをアプリケーション全体で一括管理できます。これにより、個々のコンポーネントにテーマ情報を直接渡す必要がなくなり、以下のような利点が得られます。

  • 一貫性の向上:アプリ全体で統一されたテーマを適用できます。
  • 変更の容易さ:テーマ変更時に、すべての関連コンポーネントが自動的に更新されます。

コードの簡素化と保守性向上

テーマ情報をuseContext()で簡単に取得できるため、プロップスの受け渡しが不要になります。これにより、次のような効果が得られます。

  • プロップスドリリングの回避:深い階層のコンポーネントにテーマ情報を渡す必要がなくなります。
  • 保守性の向上:テーマロジックが一箇所に集中するため、管理が容易になります。

動的テーマ変更の実現

ユーザーの操作に応じてテーマカラーを動的に切り替える機能を、簡単に実装できます。例えば、ダークモードの切り替えやカラーパレットの選択などをスムーズに行えます。

const { theme, setTheme } = useContext(ThemeContext);

// テーマ変更関数
function toggleTheme() {
  setTheme(theme === 'light' ? 'dark' : 'light');
}

// 使用例
<button onClick={toggleTheme}>Switch Theme</button>

再利用性の向上

テーマカラー管理はカスタムフックやプロバイダーとして実装することで、他のプロジェクトでも再利用可能です。これにより、異なるアプリケーション間での一貫性も確保できます。

複雑な状態管理が不要

テーマ管理をReduxや他の状態管理ツールで実装する場合に比べて、コンテキストAPIは簡素な設定で済むため、導入が容易で、コード量も削減できます。

コンテキストAPIを使用したテーマカラー管理は、Reactアプリケーションにおけるテーマ適用の煩雑さを大幅に軽減し、モダンで洗練されたUI開発を可能にします。

カスタムテーマコンテキストの作成方法

ReactのコンテキストAPIを使用して、アプリケーション全体で使用可能なカスタムテーマコンテキストを作成するプロセスを解説します。この設定により、テーマカラーの管理が効率化され、テーマの一貫性が保証されます。

1. コンテキストの作成

React.createContext()を使用して、テーマ用のコンテキストを作成します。これにより、アプリケーション全体でテーマ情報を共有できる基盤が整います。

import React, { createContext } from 'react';

// テーマコンテキストの作成
const ThemeContext = createContext();

// コンテキストのエクスポート
export default ThemeContext;

2. コンテキストプロバイダーの設定

コンテキストプロバイダーは、テーマの状態や変更機能を提供する役割を果たします。このプロバイダーをアプリケーションのルートに設置します。

import React, { useState } from 'react';
import ThemeContext from './ThemeContext';

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light'); // 初期テーマを設定

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

3. アプリケーション全体への適用

ThemeProviderをアプリケーションのルートコンポーネントで使用し、すべての子コンポーネントがテーマ情報を利用できるようにします。

import React from 'react';
import { ThemeProvider } from './ThemeProvider';
import AppContent from './AppContent';

function App() {
  return (
    <ThemeProvider>
      <AppContent />
    </ThemeProvider>
  );
}

export default App;

4. コンテキストの利用

useContext()フックを使用して、子コンポーネントからテーマ情報を簡単に取得できます。

import React, { useContext } from 'react';
import ThemeContext from './ThemeContext';

function ThemedComponent() {
  const { theme } = useContext(ThemeContext);

  return (
    <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
      現在のテーマ: {theme}
    </div>
  );
}

export default ThemedComponent;

まとめ

この手順で作成したカスタムテーマコンテキストを利用すれば、Reactアプリケーション全体で統一されたテーマ管理が可能になります。コンテキストAPIを使用することで、テーマロジックが簡潔にまとまり、メンテナンス性も向上します。

カスタムテーマプロバイダーの実装

Reactのテーマ管理を効率化するために、カスタムテーマプロバイダーを実装します。これにより、アプリケーション全体でテーマ情報を一括管理し、統一感のあるUIを実現できます。

1. プロバイダーの役割

テーマプロバイダーは、アプリケーション全体にテーマに関連する状態や機能を供給します。このプロバイダーを介して、全コンポーネントが同じテーマ情報を利用できるようになります。

2. プロバイダーの実装手順

useStateを活用してテーマ状態を管理し、それをThemeContext.Providerを通じて渡します。

import React, { useState, useContext, createContext } from 'react';

// テーマコンテキストの作成
const ThemeContext = createContext();

// プロバイダーの作成
export function ThemeProvider({ children }) {
  // 初期テーマ設定
  const [theme, setTheme] = useState('light');

  // テーマ切り替え機能
  const toggleTheme = () => {
    setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// コンテキストを使用するためのカスタムフック
export function useTheme() {
  return useContext(ThemeContext);
}

3. アプリケーションにプロバイダーを適用

アプリケーション全体でテーマプロバイダーを使うには、ルートコンポーネントでラップします。

import React from 'react';
import { ThemeProvider } from './ThemeProvider';
import AppContent from './AppContent';

function App() {
  return (
    <ThemeProvider>
      <AppContent />
    </ThemeProvider>
  );
}

export default App;

4. テーマを利用するコンポーネントの実装

useThemeカスタムフックを利用して、簡単にテーマ情報を取得できます。

import React from 'react';
import { useTheme } from './ThemeProvider';

function ThemedButton() {
  const { theme, toggleTheme } = useTheme();

  return (
    <button
      onClick={toggleTheme}
      style={{
        backgroundColor: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#000' : '#fff',
        border: 'none',
        padding: '10px 20px',
        cursor: 'pointer',
      }}
    >
      現在のテーマ: {theme}
    </button>
  );
}

export default ThemedButton;

5. プロバイダーのカスタマイズ

プロバイダーに初期値や追加機能を簡単に追加できます。たとえば、初期テーマをローカルストレージから取得することで、ユーザー設定を永続化することも可能です。

const storedTheme = localStorage.getItem('theme') || 'light';

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState(storedTheme);

  const toggleTheme = () => {
    const newTheme = theme === 'light' ? 'dark' : 'light';
    setTheme(newTheme);
    localStorage.setItem('theme', newTheme);
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

まとめ

テーマプロバイダーを実装することで、テーマ管理がシンプルかつ柔軟になります。プロバイダーは、アプリ全体の統一感を保ちつつ、動的なテーマ切り替えを可能にし、ユーザーエクスペリエンスを向上させる重要な役割を果たします。

テーマ切り替えロジックの実装例

Reactを使って、テーマを動的に切り替えるロジックを構築します。このセクションでは、ボタンをクリックすることでテーマを「ライトモード」から「ダークモード」に変更する方法を実装します。

1. テーマ切り替えの基本ロジック

テーマの状態をuseStateフックで管理し、setTheme関数を使用して動的にテーマを変更します。これをプロバイダーから供給し、コンポーネント内で利用します。

import React from 'react';
import { useTheme } from './ThemeProvider';

function ThemeToggleButton() {
  const { theme, toggleTheme } = useTheme();

  return (
    <button
      onClick={toggleTheme}
      style={{
        backgroundColor: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#000' : '#fff',
        padding: '10px 20px',
        borderRadius: '5px',
        border: '1px solid',
        cursor: 'pointer',
      }}
    >
      現在のテーマ: {theme}(クリックで切り替え)
    </button>
  );
}

export default ThemeToggleButton;

2. プロバイダーでの切り替えロジック

テーマの切り替えロジックをプロバイダーに追加します。このロジックは、現在のテーマをチェックし、lightならdarkdarkならlightに切り替えます。

export function ThemeProvider({ children }) {
  const [theme, setTheme] = React.useState('light'); // 初期テーマ

  const toggleTheme = () => {
    setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

3. テーマを適用するスタイルの構造

テーマの状態に応じて、スタイルを動的に変更します。これにより、アプリケーション全体の外観がリアルタイムで変化します。

function ThemedAppContainer() {
  const { theme } = useTheme();

  return (
    <div
      style={{
        backgroundColor: theme === 'light' ? '#f0f0f0' : '#121212',
        color: theme === 'light' ? '#000' : '#fff',
        height: '100vh',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        transition: 'background-color 0.3s ease, color 0.3s ease',
      }}
    >
      <h1>現在のテーマは {theme} モードです</h1>
    </div>
  );
}

export default ThemedAppContainer;

4. 完全な使用例

これまでのコードを組み合わせて、テーマの切り替え機能を持つアプリケーションを完成させます。

import React from 'react';
import { ThemeProvider } from './ThemeProvider';
import ThemeToggleButton from './ThemeToggleButton';
import ThemedAppContainer from './ThemedAppContainer';

function App() {
  return (
    <ThemeProvider>
      <ThemedAppContainer />
      <ThemeToggleButton />
    </ThemeProvider>
  );
}

export default App;

5. ユーザーエクスペリエンスの向上

  • スムーズな切り替え: スタイルにtransitionを適用して、背景や文字色の変化を滑らかにします。
  • アクセシビリティの強化: コントラスト比を考慮し、明暗テーマの視認性を向上させます。

まとめ

この実装例を活用すれば、Reactアプリケーションで簡単にテーマを動的に切り替えることが可能になります。直感的なUIとスムーズなテーマ変更は、ユーザー体験の向上に大きく貢献します。

ユーザー設定を保存する方法

Reactで実装したカスタムテーマ設定をユーザーが選択した状態で維持するために、ローカルストレージを利用して設定を保存します。この方法により、アプリケーションを再読み込みした場合でも、最後に選択したテーマを保持できます。

1. ローカルストレージを利用する理由

ローカルストレージは、ブラウザに永続的なデータを保存できる仕組みです。以下の特徴から、ユーザー設定の保存に最適です。

  • データ永続性: ユーザーがブラウザを閉じても設定が保持されます。
  • シンプルなAPI: キーと値のペアで簡単にデータを保存・取得できます。

2. 初期テーマをローカルストレージから取得

useStateフックの初期値をローカルストレージの値に設定します。これにより、アプリケーション起動時に保存された設定を適用できます。

export function ThemeProvider({ children }) {
  // ローカルストレージからテーマを取得し、デフォルトは 'light' に設定
  const storedTheme = localStorage.getItem('theme') || 'light';
  const [theme, setTheme] = React.useState(storedTheme);

  // テーマ切り替え時にローカルストレージに保存
  const toggleTheme = () => {
    const newTheme = theme === 'light' ? 'dark' : 'light';
    setTheme(newTheme);
    localStorage.setItem('theme', newTheme); // テーマを保存
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

3. テーマを取得・保存するカスタムフック

テーマの保存処理をカスタムフックに切り出すことで、再利用性を高めます。

import { useState, useEffect } from 'react';

function usePersistedTheme(defaultTheme = 'light') {
  const [theme, setTheme] = useState(() => {
    return localStorage.getItem('theme') || defaultTheme;
  });

  useEffect(() => {
    localStorage.setItem('theme', theme);
  }, [theme]);

  return [theme, setTheme];
}

このフックをプロバイダーで活用します。

export function ThemeProvider({ children }) {
  const [theme, setTheme] = usePersistedTheme();

  const toggleTheme = () => {
    setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

4. ローカルストレージがテーマ切り替えに与える影響

アプリケーション全体でテーマの一貫性を保つために、ローカルストレージの内容を適切に利用します。以下のように、選択したテーマは次回アクセス時も適用されます。

function ThemedAppContainer() {
  const { theme } = useTheme();

  return (
    <div style={{ backgroundColor: theme === 'light' ? '#fff' : '#121212', color: theme === 'light' ? '#000' : '#fff' }}>
      現在のテーマ: {theme}
    </div>
  );
}

5. トラブルシューティング

  • ローカルストレージ未対応: 古いブラウザでは利用できない場合があるため、フォールバックを実装します。
  • 同期の問題: 複数タブでのテーマ変更を同期するために、window.addEventListener('storage', callback)を利用します。

まとめ

ローカルストレージを活用することで、ユーザーが選択したテーマ設定をアプリケーション全体で永続化できます。このアプローチにより、ユーザーは再訪時にも一貫した体験を享受でき、カスタムテーマ機能の価値がさらに高まります。

実用的なアプリケーション例

Reactで実装したカスタムテーマ機能を活用することで、ユーザーにとって便利で魅力的なアプリケーションを構築できます。このセクションでは、具体的な応用例として、ダークモード切り替えが可能なタスク管理アプリケーションを紹介します。

1. アプリケーション概要

本例では、以下の機能を備えたシンプルなタスク管理アプリを構築します。

  • ライトモードとダークモードの切り替え
  • ユーザーが選択したテーマの保存と復元
  • タスクリストの追加と削除

2. テーマ適用の全体構造

テーマ切り替え機能をアプリケーション全体に適用し、タスク管理画面の見た目をダイナミックに変える実装例です。

import React from 'react';
import { ThemeProvider, useTheme } from './ThemeProvider';
import TaskManager from './TaskManager';

function App() {
  return (
    <ThemeProvider>
      <ThemedApp />
    </ThemeProvider>
  );
}

function ThemedApp() {
  const { theme, toggleTheme } = useTheme();

  return (
    <div
      style={{
        backgroundColor: theme === 'light' ? '#f9f9f9' : '#1e1e1e',
        color: theme === 'light' ? '#000' : '#fff',
        minHeight: '100vh',
        padding: '20px',
      }}
    >
      <header>
        <h1>タスク管理アプリ</h1>
        <button onClick={toggleTheme} style={{ margin: '10px 0' }}>
          {theme === 'light' ? 'ダークモードに切り替え' : 'ライトモードに切り替え'}
        </button>
      </header>
      <TaskManager />
    </div>
  );
}

export default App;

3. タスク管理の機能実装

タスク管理のコンポーネントを作成し、テーマに応じたスタイルを適用します。

import React, { useState } from 'react';

function TaskManager() {
  const [tasks, setTasks] = useState([]);
  const [task, setTask] = useState('');

  const addTask = () => {
    if (task.trim()) {
      setTasks([...tasks, task]);
      setTask('');
    }
  };

  const deleteTask = (index) => {
    setTasks(tasks.filter((_, i) => i !== index));
  };

  return (
    <div>
      <input
        type="text"
        value={task}
        onChange={(e) => setTask(e.target.value)}
        placeholder="タスクを入力"
        style={{
          padding: '10px',
          width: '80%',
          margin: '10px 0',
        }}
      />
      <button onClick={addTask} style={{ padding: '10px 20px' }}>
        タスクを追加
      </button>
      <ul>
        {tasks.map((task, index) => (
          <li key={index} style={{ margin: '10px 0' }}>
            {task}
            <button
              onClick={() => deleteTask(index)}
              style={{
                marginLeft: '10px',
                padding: '5px 10px',
                backgroundColor: 'red',
                color: '#fff',
                border: 'none',
              }}
            >
              削除
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default TaskManager;

4. スタイルの適用

テーマによって動的に切り替わる背景色やテキスト色を設定します。useThemeを利用して現在のテーマを取得し、UIに反映させます。

function TaskItem({ task, onDelete }) {
  const { theme } = useTheme();

  return (
    <li
      style={{
        padding: '10px',
        margin: '5px 0',
        backgroundColor: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#000' : '#fff',
        borderRadius: '5px',
        display: 'flex',
        justifyContent: 'space-between',
        alignItems: 'center',
      }}
    >
      {task}
      <button
        onClick={onDelete}
        style={{
          backgroundColor: theme === 'light' ? '#ff4d4d' : '#ff6666',
          color: '#fff',
          border: 'none',
          padding: '5px 10px',
          cursor: 'pointer',
        }}
      >
        削除
      </button>
    </li>
  );
}

5. 機能の拡張

  • テーマ選択のカスタマイズ: ユーザーがライト、ダーク以外のテーマも選択できるように、カラーパレットを導入します。
  • ローカルストレージへのタスク保存: localStorageを利用してタスク情報も永続化します。

まとめ

このアプリケーション例を参考に、カスタムテーマ機能をリアルなプロジェクトに取り入れることで、ユーザーの利便性とアプリケーションの見栄えを大幅に向上させることが可能です。テーマ切り替えと動的なスタイリングの組み合わせは、モダンなUIの開発における必須技術と言えます。

デバッグとトラブルシューティング

カスタムテーマ管理機能をReactで実装する際に直面する可能性のある問題と、それらを解決する方法を解説します。テーマ切り替え機能の動作確認を通じて、アプリケーションの信頼性を高めることができます。

1. 問題: テーマが正しく適用されない

原因: useContextでコンテキストを正しく取得できていない、またはテーマの状態が正しく更新されていない可能性があります。

解決策:

  1. コンテキストがプロバイダーで正しくラップされているか確認します。
    jsx <ThemeProvider> <YourComponent /> </ThemeProvider>
  2. プロバイダーの値が適切に設定されているか確認します。
    jsx <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider>

デバッグ手順:

  • console.logを使い、themeの状態やtoggleThemeが呼び出されているかを確認します。
  • ブラウザの開発者ツールでDOMのスタイルをチェックして、テーマが正しく反映されているか確認します。

2. 問題: ローカルストレージがテーマを保存しない

原因: localStorageの操作に関するコードが間違っている、または状態の変更が反映されていない可能性があります。

解決策:

  1. localStorageの保存と取得が正しく実装されているか確認します。
    jsx localStorage.setItem('theme', theme); const savedTheme = localStorage.getItem('theme');
  2. useEffectを利用して、テーマが変更されたときにローカルストレージを更新します。
    jsx useEffect(() => { localStorage.setItem('theme', theme); }, [theme]);

デバッグ手順:

  • localStorageの内容をブラウザの開発者ツールで確認します。
  • スクリプトの実行時にエラーが出ていないか確認します。

3. 問題: 複数タブでテーマの同期ができない

原因: ローカルストレージの変更が他のタブに反映されない。

解決策:

  1. storageイベントリスナーを追加して、ローカルストレージの変更を監視します。
    jsx useEffect(() => { const handleStorageChange = (event) => { if (event.key === 'theme') { setTheme(event.newValue); } }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, []);

デバッグ手順:

  • 2つのタブを開いて、テーマを切り替えた際に同期が行われるか確認します。

4. 問題: スタイルが動的に更新されない

原因: スタイルがテーマの状態に依存していない、またはuseEffectが適切に設定されていない可能性があります。

解決策:

  1. テーマに依存したスタイルが正しく設定されているか確認します。
    jsx const { theme } = useTheme(); const backgroundColor = theme === 'light' ? '#fff' : '#000';
  2. transitionをCSSに追加してスムーズな切り替えを実現します。
    css transition: background-color 0.3s ease, color 0.3s ease;

デバッグ手順:

  • ブラウザの開発者ツールを使用して、スタイルの変更をリアルタイムで確認します。

5. 問題: 初期値が意図しない値になっている

原因: ローカルストレージが空の場合に初期値を適切に設定していない可能性があります。

解決策:

  1. 初期値を設定する際にフォールバックの値を指定します。
    jsx const storedTheme = localStorage.getItem('theme') || 'light'; const [theme, setTheme] = useState(storedTheme);

デバッグ手順:

  • ローカルストレージの初期状態と、useStateの初期値を確認します。

まとめ

Reactでカスタムテーマ管理を実装する際には、デバッグとトラブルシューティングを行いながら正確な動作を確認することが重要です。特に、コンテキストやローカルストレージの設定には注意を払い、同期やスタイルの適用に問題がないか確認することで、ユーザーにスムーズな体験を提供できます。

まとめ

本記事では、Reactを活用したカスタムテーマカラーの設定方法を解説しました。コンテキストAPIを利用することで、テーマ管理が効率化され、プロジェクト全体で統一されたデザインを実現できます。また、ローカルストレージを活用することで、ユーザーが選択した設定を永続化し、利便性を向上させる方法を紹介しました。

動的なテーマ切り替え機能を実装することで、モダンなユーザーインターフェースを提供しつつ、アクセシビリティやカスタマイズ性を高めることが可能です。ReactのコンテキストAPIとテーマプロバイダーを適切に活用し、より優れたユーザー体験を目指しましょう。

コメント

コメントする

目次