React の状態管理を簡単にしたい！F-Box の基本の完全ガイド

## **1. はじめに**

TypeScript を使った開発で、状態管理や非同期処理、例外処理などが複雑になり、コードの保守性に悩むことはありませんか？

**F-Box** は、TypeScript 用のライブラリで、状態や処理を型安全に扱える仕組みを提供します。また、その応用で、**React の状態管理も**行うことができます。

### **この記事で学べること**

- F-Box を使った基本的な状態管理の方法
- React コンポーネント内での F-Box の具体的な活用例
- Todo アプリを作りながら、F-Box の便利さを体感する

シンプルなサンプルを通して、F-Box を無理なく使い始められる内容にしています。
それでは早速見ていきましょう！

## **2. F-Box とは？**

React での **状態管理** と聞くと、「Redux みたいなもの？」「Jotai の仲間？」と思うかもしれません。
F-Box はこれらとは少し異なり、**状態管理、非同期処理、例外処理** を一貫したコンセプトで扱えるツールです。

### **ドキュメント**

https://f-box-docs.com

### **2.1 F-Box の特徴**

F-Box は、5 つの「Box」と呼ばれる構造を使い、データや状態を効率的に操作します。
これにより、状態管理や非同期処理、例外処理を統一的に扱える仕組みを提供しています。
これはコア部分（`f-box-core`）としてバックエンドも含めた TypeScript 環境であればどこでも利用可能です。
またコアとは別で React 用のフックライブラリ（`f-box-react`）が用意されているので、フロントエンドまで一貫した開発体験が可能になります。

### **2.2 F-Box が解決する課題**

ここでは F-Box がどのような場面で役立つのかを簡単な例とともに見ていきます。

#### **1. 状態管理をシンプルに**

React では、コンポーネント間で状態を共有しようとすると、Props の受け渡しが煩雑になることがあります。
F-Box の **`RBox`** を使えば、状態をラップして管理し、複数のコンポーネント間で簡単に共有できます。
さらに、**`useRBox` フック** を使うことで、RBox の状態の変更を React のライフサイクルに統合させることができます。

**例: 状態の共有**

以下は、React でカウンター状態を共有する例です。

```tsx
import { RBox } from "f-box-core"
import { useRBox, set } from "f-box-react"

// 状態を管理するRBoxを作成
const countBox = RBox.pack(0)

function Parent() {
  const [count] = useRBox(countBox) // RBoxから現在の値を取得
  return (
    <div>
      <p>Count: {count}</p>
      <Child />
    </div>
  )
}

function Child() {
  const [count] = useRBox(countBox) // 同じRBoxを参照
  const setCount = set(countBox) // 状態を更新する関数を取得
  return (
    <button onClick={() => setCount(count + 1)}>
      Increment (Current: {count})
    </button>
  )
}
```

この例では、`countBox` を介して親コンポーネントと子コンポーネントが同じ状態を共有しています。
状態の共有に `Context` や `Provider` は必要ありません。
`useRBox` を使って RBox の中の値をリアクティブに参照できるようになり、RBox を更新することでコンポーネントが再レンダリングされます。

#### **2. 非同期処理とエラー処理を一元管理**

非同期処理やエラー処理をシンプルに管理するために、F-Box では以下の仕組みを提供しています。

1. 非同期処理をラップし、実行タイミングを明確に制御。
2. 成功と失敗の状態を統一的に扱い、エラーや例外を整理。
3. 値が「存在する」か「存在しない」かを安全に確認。

**例: 非同期処理**

以下は、API から文字列データを取得し、状態として保存する例です。

```tsx
import { Task, RBox, set } from "f-box-core"

// 状態の初期値を設定
const dataBox = RBox.pack("No data available.")

// 非同期処理をラップ
const fetchData = Task.tryCatch(
  async () => {
    const response = await fetch("https://api.example.com/data")
    if (!response.ok) throw new Error("Failed to fetch.")
    return await response.text() // 成功時
  },
  (error) => error.message // 失敗時
)

// 非同期処理を実行し、結果を状態に保存
fetchData["<$>"](set(dataBox)).run()
```

**例: 値の有無を確認する**

以下は、`Maybe` 型を使って値が「存在する」か「存在しない」かを安全に扱う例です。

```tsx
import { Maybe } from "f-box-core"

// 値が存在する場合
const maybeValue = Maybe.just("Hello, F-Box!")

// 値が存在しない場合
const noValue = Maybe.nothing()

// 値の確認
const result1 = maybeValue.match(
  (value) => `Value exists: ${value}`, // 値がある場合
  () => "No value available" // 値がない場合
)

const result2 = noValue.match(
  (value) => `Value exists: ${value}`, // 値がある場合
  () => "No value available" // 値がない場合
)

console.log(result1) // Value exists: Hello, F-Box!
console.log(result2) // No value available
```

詳細な型の説明や、それぞれの型がどのように課題を解決するのかについては、次の章で詳しく解説します！

## **3. F-Box の主要なコンセプト**

### **3.1 F-Box が提供する 5 つの Box 型**

F-Box では、値や状態をラップして管理するための **5 つの Box 型** が提供されています。
これらの型を使うことで、値や状態を安全かつ効率的に操作でき、エラーや非同期処理の複雑さを軽減することが可能です。

1. **Box**: 最もシンプルな型で、値をラップして操作を提供します。
2. **RBox**: リアクティブな状態管理を可能にする型です。
3. **Task**: 非同期処理をラップして管理します。
4. **Maybe**: 値が「存在する」か「存在しない」かを扱う型です。
5. **Either**: 処理結果を「成功」と「失敗」に分けて管理します。

以下では、これらの型の特徴と具体的な使い方を詳しく見ていきます。

### **3.2 各 Box 型の役割と使い方**

#### **1. Box: 基本的な値のラップ**

**`Box`** は、最も基本的な型で、値をラップして基本的な操作を可能にします。

- **用途**: 単純な値の操作。
- **主なメソッド**:
  - `map`: 値に関数を適用し、新しい Box を返します。

**例: 値のラップと操作**

```tsx
import { Box } from "f-box-core"

const numberBox = Box.pack(5)

// 値に関数を適用
const doubledBox = numberBox.map((n) => n * 2)

console.log(doubledBox.getValue()) // 10
```

#### **2. RBox: 状態管理の中核**

**`RBox`**（Reactive Box）は、状態をラップしてリアクティブに管理する型です。
状態が変更されると、自動的に購読者に通知されます。

- **用途**: 状態の共有やリアクティブな通知。
- **主なメソッド**:
  - `subscribe`: 状態変更時に呼ばれる関数を登録。
  - `setValue`: 状態を更新。

**例: 状態の管理と通知**

```tsx
import { RBox } from "f-box-core"

const countBox = RBox.pack(0)

// 状態の変更を購読
countBox.subscribe((newValue) => {
  console.log("New value:", newValue)
})

// 状態を更新
countBox.setValue((prev) => prev + 1) // コンソールに "New value: 1" が出力される
```

#### **3. Task: 非同期処理のラップ**

**`Task`** は、**非同期処理をラップする型**です。
直接 `Promise` を扱うのではなく、**`<T>() => Promise<T>`** の形式でラップすることで、**実行タイミングを遅延**させ、必要なタイミングで非同期処理を評価できるのが特徴です。

- **用途**: 非同期処理のラップ（例: API 呼び出し）。
- **主なメソッド**:
  - `tryCatch`: 非同期処理を安全にラップ。
  - `run`: 非同期処理を実行。

**例: 非同期処理の実行**

```tsx
import { Task } from "f-box-core"

// 非同期処理をラップ
const fetchData = Task.tryCatch(
  async () => {
    const response = await fetch("https://api.example.com/data")
    if (!response.ok) throw new Error("Failed to fetch.")
    return await response.text()
  },
  (error) => `Error: ${error.message}`
)

// run で非同期処理を実行（このタイミングで Promise を評価する）
fetchData.run().then((result) => console.log("Result:", result))
```

#### **4. Maybe: オプショナルな値の管理**

**`Maybe`** は、値が「存在する」か「存在しない」かを安全に扱う型です。

- **用途**: オプショナルな値の操作（例: API レスポンスや入力値）。
- **主なメソッド**:
  - `match`: 値がある場合とない場合の処理を分けます。

**例: 値の存在確認**

```tsx
import { Maybe, RBox } from "f-box-core"
import { useRBox } from "f-box-react"

// 状態の初期値を設定
const maybeBox = RBox.pack<Maybe<string>>(Maybe.just("Hello, F-Box!"))

function Example() {
  const [maybeValue] = useRBox(maybeBox)

  return maybeValue.match(
    (value) => <p>Value exists: {value}</p>, // 値がある場合
    () => <p>No value available</p> // 値がない場合
  )
}
```

#### **5. Either: 成功と失敗の明確化**

**`Either`** は、処理結果を「成功」か「失敗」に分けて管理する型です。
型引数として **`L`（Left: 失敗時の型）** と **`R`（Right: 成功時の型）** を受け取ります。

- **用途**: 処理結果の成功と失敗を明確に区別したい場合。
- **主なメソッド**:
  - `match`: 成功と失敗の処理を分ける。

**例: 処理結果の区別**

以下は、`Either<string, string>` を使用して、成功時と失敗時の状態を表示する例です。

```tsx
import { Either, RBox } from "f-box-core"
import { useRBox } from "f-box-react"

// 状態の初期値を設定
// L: 失敗時の型（string）
// R: 成功時の型（string）
const resultBox = RBox.pack<Either<string, string>>(
  Either.right("Operation successful")
)

function Example() {
  const [result] = useRBox(resultBox)

  return result.match(
    (error) => <p style={{ color: "red" }}>Error: {error}</p>, // 失敗時の表示
    (success) => <p style={{ color: "green" }}>Success: {success}</p> // 成功時の表示
  )
}
```

**補足: Either の型引数**

- **`L`（Left）**: 失敗時の型（エラーメッセージや例外情報など）。
- **`R`（Right）**: 成功時の型（処理結果など）。

`Either<L, R>` の `match` メソッドを使用すると、
成功（Right）と失敗（Left）の処理を明確に分けて記述できます。

### **3.3 F-Box の基本操作**

F-Box のこれら **5 つの Box 型** は、値の変換や状態の組み合わせを柔軟に行える **演算子（メソッドのエイリアス）** をサポートしています。
これにより、既存の Box を基に新しい派生状態を簡潔に定義でき、状態管理を直感的に記述できます。

これらの演算子は、通常のメソッド（例: `map`）に対応する `<$>` などのエイリアスとして提供されています。
演算子を利用することで、意図が明確で可読性の高いコードを記述でき、複雑な状態管理のチェーン処理も自然に表現可能です。

以下では、F-Box の主要な演算子である `map`（`<$>`), `apply`（`<*>`), `flatMap`（`>>=`) の具体例を紹介します。これらのメソッドはすべての Box 型に共通です。

#### **1. map（`<$>`): 派生 Box を作る**

`map`は、Box 型にラップされた値を基に新しい派生 Box を作成します。
元の Box は変更されず、新しい Box が返されます。

```tsx
import { RBox } from "f-box-core"

const countBox = RBox.pack(10)

// 値を2倍にする派生Boxを作成
const doubleCountBox = countBox["<$>"]((count) => count * 2)
// または通常のメソッド呼び出し形式
const doubleCountBoxAlt = countBox.map((count) => count * 2)

console.log(countBox.getValue()) // 10 (元のBox)
console.log(doubleCountBox.getValue()) // 20 (派生Box)
console.log(doubleCountBoxAlt.getValue()) // 20 (派生Box)
```

#### **2. apply（`<*>`): 複数の Box 型から派生 Box を作る**

`apply`は、複数の Box 型を組み合わせて新しい派生 Box を作成します。
まず関数を Box 型にラップし、それを`apply`で他の Box 型に適用します。

```tsx
import { RBox } from "f-box-core"

const addBox = RBox.pack((a: number) => (b: number) => a + b) // 加算関数をBox型にラップ
const aBox = RBox.pack(10)
const bBox = RBox.pack(20)

// 2つの値を加算する派生Boxを作成（アプリカティブスタイル）
const sumBox = addBox["<*>"](aBox)["<*>"](bBox)
// または通常のメソッド呼び出し形式
const sumBoxAlt = addBox.apply(aBox).apply(bBox)

console.log(sumBox.getValue()) // 30
console.log(sumBoxAlt.getValue()) // 30
```

#### **3. flatMap（`>>=`): 動的な派生 Box を作る**

`flatMap`は、元の Box の値に基づいて動的に新しい Box を生成し、それを返します。
動的な依存関係や条件分岐を扱う際に便利で、複雑なロジックを簡潔に記述できます。

```tsx
import { RBox } from "f-box-core"

const isEvenBox = RBox.pack(true) // true: 偶数, false: 奇数
const evenBox = RBox.pack("Even")
const oddBox = RBox.pack("Odd")

// 条件に応じて動的にBoxを切り替える派生Boxを作成
const resultBox = isEvenBox[">>="]((isEven) => (isEven ? evenBox : oddBox))
// または通常のメソッド呼び出し形式
const resultBoxAlt = isEvenBox.flatMap((isEven) => (isEven ? evenBox : oddBox))

// 値の確認
console.log(resultBox.getValue()) // "Even" (初期状態)

// 動的に切り替わる
isEvenBox.setValue(false)
console.log(resultBox.getValue()) // "Odd" (isEvenBoxがfalseに変更)
```

F-Box は、通常のメソッド呼び出し形式と、`<$>`, `<*>`, `>>=` といった **演算子形式** の両方をサポートしています。

特に演算子形式は、状態管理の操作を簡潔かつ意図が明確に記述できるため、複数の操作をチェーンする場合に有用です。

また、`["<$>"]` のような記法での記述は VSCode などで通常のメソッドと同様にサジェストされるため、直感的に使用できます。

![image-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43936/825c340b-7346-1b44-7d7d-d7428506c468.png)

:::note info
**※補足**
各演算子はそれぞれ、`<$>` は Functor、`<*>` は Applicative、`>>=` は Monad という代数的構造の概念に基づいています。
F-Box を利用するうえでこれらの概念を理解しておく必要はありませんが、背景としてこれらの概念について興味のある方は、以下の記事でわかりやすく説明されているので参考にしてください。
:::

https://qiita.com/suin/items/0255f0637921dcdfe83b

次章では、これらの操作を React でどのように活用するかを詳しく解説します！

## **4. React での F-Box の使い方**

### **4.1 F-Box React とは？**

F-Box React は、React で F-Box を活用するための拡張ライブラリです。
RBox を React フックとして扱い、React コンポーネント内で直感的かつリアクティブな状態管理が可能になります。

#### **主な特徴**

1. **`useRBox`フック**を使った型安全で直感的な状態管理。
2. 派生状態を効率的に作成可能。
3. 再レンダリングの最適化を考慮した設計。

### **4.2 `useRBox` の基本構造**

`useRBox`は、以下の 4 つの使い方が可能です：

1. プリミティブ値を初期値として管理する。
2. 既存の RBox を利用する。
3. 派生状態を効率的に作成する。
4. 複数の状態を組み合わせた派生状態を作成する。

これらの使い方を順に見ていきます。

### **4.3 `useRBox` の基本パターン**

#### **1. プリミティブ値を管理する**

プリミティブな値を直接`useRBox`に渡すと、新しい RBox が作成され、その現在値と RBox インスタンスが返されます。

```tsx
function Counter() {
  const [count, countBox] = useRBox(0) // 初期値0でRBoxを生成

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => countBox.setValue((prev) => prev + 1)}>+1</button>
    </div>
  )
}
```

#### **2. 既存の RBox を利用する**

すでに定義されている RBox を`useRBox`に渡して利用します。この場合、RBox 自体はそのまま利用されます。

```tsx
const countBox = RBox.pack(0)

function Counter() {
  const [count] = useRBox(countBox)

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => countBox.setValue((prev) => prev + 1)}>+1</button>
    </div>
  )
}
```

#### **3. 派生状態を効率的に作成する**

`useRBox`に関数を渡すことで、派生状態を作成する際に計算を遅延評価し、効率的にメモ化します。これにより、不要な再計算を防ぎつつ、派生状態を簡潔に記述できます。

```tsx
function Counter() {
  const [count, countBox] = useRBox(0) // 初期値0で生成
  const [isEven, isEvenBox] = useRBox(() => countBox["<$>"]((v) => v % 2 === 0))

  return (
    <div>
      <p>Count: {count}</p>
      <p>Is Even: {isEven ? "Yes" : "No"}</p>
      <button onClick={() => countBox.setValue((prev) => prev + 1)}>+1</button>
    </div>
  )
}
```

#### **4. 複数の状態を組み合わせて派生状態を作成する**

複数の RBox を組み合わせて派生状態を作成する場合、アプリカティブスタイル（`apply`）を利用します。

```tsx
const itemCountBox = RBox.pack(2) // 商品数
const itemPriceBox = RBox.pack(100) // 商品単価
const calculateTotal = (count: number) => (price: number) => count * price

function ShoppingCart() {
  const [totalPrice] = useRBox(() =>
    RBox.pack(calculateTotal)["<*>"](itemCountBox)["<*>"](itemPriceBox)
  )

  return (
    <div>
      <p>合計金額: {totalPrice}円</p>
    </div>
  )
}
```

F-Box の`useRBox`を活用することで、React アプリケーション内での状態管理が以下のように効率化されます

- **シンプルな状態管理**: プリミティブ値や既存の RBox を簡単に扱える。
- **派生状態の作成**: 遅延評価とメモ化を活用して、効率的な派生状態を作成。

次章では、これらを応用して、実践的な Todo アプリの作成に挑戦します！

## **5. チュートリアル：F-Box で Todo アプリを作る**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43936/fee866ea-c1f3-0dc7-7318-6a67d93fafbd.png)

F-Box の特徴を活用する最良の方法は、実際にアプリを作ってみることです。この章では、React と F-Box を使ってシンプルな Todo アプリを作成します。
以下のような機能を実装します

1. **タスクの追加**: 新しいタスクをリストに追加。
2. **タスクの完了**: タスクの状態を「完了」に更新。
3. **タスクの削除**: タスクをリストから削除。
4. **タスクのフィルタリング**: 完了/未完了/全ての状態でタスクを絞り込み。

### **5.1 環境構築**

#### **プロジェクトのセットアップ**

1. **Vite を使用して React プロジェクトを作成**
   Vite は、フロントエンドアプリケーションを高速にセットアップできるツールです。以下のコマンドでプロジェクトを作成します

```bash
npm create vite@latest f-box-todo-app --template react-ts
cd f-box-todo-app
```

2. **React のバージョンに応じた F-Box のインストール**
   React のバージョンによって使用する `f-box-react` のバージョンが異なります。以下を参考にしてください：

   - **React v19** を使用する場合：

     ```bash
     npm install react@latest react-dom@latest
     npm install -D @types/react@latest @types/react-dom@latest
     npm install f-box-core f-box-react@latest
     ```

     または

     ```bash
     npm install f-box-react@0.2
     ```

   - **React v18** を使用する場合：
     ```bash
     npm install react@18 react-dom@18
     npm install -D @types/react@18 @types/react-dom@18
     npm install f-box-core f-box-react@0.1
     ```

3. **ESLint のルール設定**
   以下のルールを追加することで、マルチライン記述時のエラー回避が可能です：

```json
{
  "rules": {
    "no-unexpected-multiline": "off"
  }
}
```

4. **開発サーバーの起動**
   設定が完了したら、以下のコマンドで開発サーバーを起動します

```bash
npm run dev
```

### **5.2 グローバル状態の定義**

`state.ts` ファイルを作成し、以下の内容を記述してください：

```tsx
// state.ts
// グローバル状態と派生状態の定義
import { RBox } from "f-box-core"

// Todoの型定義
export type Todo = {
  id: number
  text: string
  completed: boolean
  date: Date
}

// フィルタータイプの型定義
export type FilterType = "all" | "completed" | "incomplete"

// タスクリスト全体の状態を管理するRBox
export const todosBox = RBox.pack<Todo[]>([])

// 現在のフィルタータイプの状態を管理するRBox
export const filterBox = RBox.pack<FilterType>("all")

// フィルタータイプに基づいてTodoをフィルタリングする関数
const todosFilter = (type: FilterType) => (todos: Todo[]) =>
  todos.filter((todo) => todo.completed === (type === "completed"))

// フィルタータイプからフィルタリングが必要かどうかを判定する派生RBox
const shouldFilterBox = filterBox["<$>"]((type) => type !== "all")

// フィルタリングされたTodoリストを取得する派生RBox
export const filteredTodosBox = shouldFilterBox[">>="]((shouldFilter) =>
  shouldFilter
    ? // フィルタリングをアプリカティブスタイルで適用
      RBox.pack(todosFilter)["<*>"](filterBox)["<*>"](todosBox)
    : // フィルタリングが不要な場合はtodosBoxをそのまま返す
      todosBox
)
```

- **タスク管理 (`todosBox`)**:
  タスクリスト全体を管理するグローバル状態です。タスクの追加、削除、更新などの操作がこの状態を通じて行われます。

- **フィルター管理 (`filterBox`)**:
  現在のフィルター条件（全て、完了済み、未完了）を保持するグローバル状態です。この条件に基づいて、表示するタスクを動的に絞り込みます。

- **派生状態 (`filteredTodosBox`)**:
  現在のフィルター条件に応じて動的に生成されるタスクリストです。
  - フィルターが「全て」の場合は、元のタスクリスト（`todosBox`）をそのまま返します。
  - それ以外の場合は、`todosFilter` を適用してフィルタリングした結果を提供します。

### **5.3 各コンポーネントの実装**

#### **1. フィルター選択**

以下のコードを `Filter.tsx` に記述します

```tsx
// components/Filter.tsx
// フィルター選択コンポーネントの実装
import { useRBox, set } from "f-box-react"
import type { FilterType } from "../state"
import { filterBox } from "../state"

const filterTypes: FilterType[] = ["all", "completed", "incomplete"]

function Filter() {
  const [filter] = useRBox(filterBox)
  const setFilter = set(filterBox)

  return (
    <div className="filter-container">
      {filterTypes.map((type) => (
        <button
          key={type}
          className={`filter-button ${filter === type ? "active" : ""}`}
          onClick={() => setFilter(type)}
        >
          {type.charAt(0).toUpperCase() + type.slice(1)}
        </button>
      ))}
    </div>
  )
}

export default Filter
```

- `filterBox` を操作してフィルター条件を変更。
- 選択状態には `active` クラスを適用して視覚的にわかりやすくしています。

#### **2. タスクの表示と更新**

以下のコードを `TodoList.tsx` に記述します

```tsx
// components/TodoList.tsx
// タスクリストの表示と更新ロジック
import { useRBox, set } from "f-box-react"
import type { Todo } from "../state"
import { todosBox, filteredTodosBox } from "../state"

const toggleComplete = (id: number) => (todo: Todo) =>
  todo.id === id ? { ...todo, completed: !todo.completed } : todo
const excludeTodo = (id: number) => (todo: Todo) => todo.id !== id

function TodoList() {
  const [filteredTodos] = useRBox(filteredTodosBox)
  const [todos] = useRBox(todosBox)
  const setTodos = set(todosBox)

  const handleToggle = (id: number) => setTodos(todos.map(toggleComplete(id)))
  const handleDelete = (id: number) => setTodos(todos.filter(excludeTodo(id)))

  return (
    <ul className="todo-list">
      {filteredTodos.map((todo) => (
        <li
          key={todo.id}
          className={`todo-item ${todo.completed ? "completed" : ""}`}
        >
          <label className="todo-label">
            <input
              type="checkbox"
              className="todo-checkbox"
              checked={todo.completed}
              onChange={() => handleToggle(todo.id)}
            />
            <div>
              <div className="todo-text">{todo.text}</div>
              <div className="todo-date">{todo.date.toLocaleString()}</div>
            </div>
          </label>
          <button
            className="trash"
            onClick={() => handleDelete(todo.id)}
            aria-label="Delete"
          ></button>
        </li>
      ))}
    </ul>
  )
}

export default TodoList
```

- **表示**: `filteredTodosBox` から現在のタスクリストを取得し、リストとして表示します。
- **更新**: チェックボックスや削除ボタンを通じてタスクの状態を更新します。

#### **3. タスクの追加**

以下のコードを `AddTodo.tsx` に記述します

```tsx
// components/AddTodo.tsx
// 新しいタスクの追加フォーム
import { useRBox, set } from "f-box-react"
import { todosBox } from "../state"

const createTodo = (text: string) => ({
  id: Date.now(),
  text,
  completed: false,
  date: new Date(),
})

function AddTodo() {
  const [text, textBox] = useRBox("")
  const setText = set(textBox)

  const addTodo = () => {
    if (text.trim() === "") return
    todosBox.setValue((todos) => [...todos, createTodo(text)])
    setText("")
  }

  return (
    <div className="add-todo-container">
      <input
        type="text"
        className="add-todo-input"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Add a task..."
      />
      <button className="add-todo-button" onClick={addTodo}>
        Add
      </button>
    </div>
  )
}

export default AddTodo
```

- `useRBox` を活用して、ローカル入力状態を管理。
- タスク追加時には現在の日付を付与して `todosBox` に格納します。

### **5.4 アプリ全体の構成**

以下のコードを `App.tsx` に記述します

```tsx
// App.tsx
// アプリ全体を構成するコンテナコンポーネント
import AddTodo from "./components/AddTodo"
import TodoList from "./components/TodoList"
import Filter from "./components/Filter"

function App() {
  return (
    <div className="app-container">
      <h1 className="app-title">TODO APP</h1>
      <AddTodo />
      <Filter />
      <TodoList />
    </div>
  )
}

export default App
```

### **5.5 デモ**

#### **デモを試す**

このアプリケーションの動作をすぐに体験したい方は、以下のリンクからデモをご覧いただけます

##### 👉 F-Box Todo App デモ

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/KentaroMorishita/f-box-todo-app)


今回の F-Box を活用した Todo アプリ開発では、以下のポイントを重点的に解説しました。

#### **1. F-Box を使った状態管理**

- **RBox** を利用することで、グローバルなタスクリストやフィルター状態を簡単に管理。
- React コンポーネント間での状態共有をスムーズに実現。

#### **2. 派生状態の活用**

- `filteredTodosBox` のような派生状態を簡潔に定義。
- 複数の状態を組み合わせることで、より柔軟な状態管理を実現。

#### **3. 実用的な React コンポーネント設計**

- 各コンポーネント（`AddTodo`、`TodoList`、`Filter`）が独立しており、再利用性の高い設計を採用。
- `useRBox` を活用して、グローバル状態とローカル状態の両方を効率的に操作。

このアプリを拡張して、より高度な機能を追加することで、F-Box の可能性をさらに広げることができます。ぜひデモを試して、自身のプロジェクトでも活用してみてください！

## **6. まとめ**

本記事では、F-Box の基本的な使い方から、React アプリケーションでの実践的な応用例として Todo アプリを作成する方法までを解説しました。

**F-Box の特徴**

- **シンプルな状態管理**: RBox を使うことで、Props のバケツリレーを解消し、直感的な状態管理が可能。
- **派生状態の効率的な作成**: 演算子形式を活用して、状態の変換や組み合わせを簡潔に記述。
- **型安全性の追求**: TypeScript を活用し、エラーを未然に防ぎながら安心して開発できる。

F-Box は、状態管理だけでなく、非同期処理や例外処理も統一的に扱えるツールとして、React をはじめ幅広い環境で活用できます。

---

**一緒に F-Box を作ってくれる人募集**

F-Box を一緒に開発してくれるコントリビューターを募集してます。
F-Box は、たった一人で孤独に開発しているまだまだ出来立てのライブラリです。
新しいアイデアや機能提案、バグ報告、ドキュメントの改善など、どんな形でも構いません。
興味を持ってくださった方は、以下の GitHub リポジトリを通じてご参加ください！

https://github.com/KentaroMorishita/f-box-core

https://github.com/KentaroMorishita/f-box-react

https://github.com/KentaroMorishita/f-box-docs