Jotai のリアクティブ思考法:コンポーネントから状態を切り離す設計哲学
React アプリケーションにおける状態管理は、アプリケーションが成長するにつれて複雑になりがちです。コンポーネントと状態が密結合すると、再利用性が低下し、テストも困難になります。Jotai は、アトミックな状態管理を通じてこの課題を解決し、コンポーネントから状態を切り離す新しい設計哲学を提供しています。
この記事では、Jotai のリアクティブ思考法を深く掘り下げ、コンポーネントと状態を分離することでどのように保守性と拡張性が向上するのかを解説します。
背景
従来の状態管理の問題点
React における従来の状態管理では、useState や useReducer を使ってコンポーネント内に状態を保持することが一般的でした。しかし、この方法にはいくつかの課題があります。
状態がコンポーネントに紐づくと、以下のような問題が発生します:
- 状態の共有が困難になり、Props のバケツリレーが発生する
- コンポーネントの責務が曖昧になり、ビジネスロジックと UI ロジックが混在する
- テスト時にコンポーネントをレンダリングしなければ状態の動作を確認できない
以下の図は、従来の状態管理におけるコンポーネント間の依存関係を示しています。
mermaidflowchart TD
App["App コンポーネント"] -->|Props 渡し| Header["Header コンポーネント"]
App -->|Props 渡し| Main["Main コンポーネント"]
App -->|Props 渡し| Sidebar["Sidebar コンポーネント"]
Main -->|Props 渡し| List["List コンポーネント"]
Main -->|Props 渡し| Detail["Detail コンポーネント"]
App -.->|useState で管理| state[("状態<br/>user, items, filters")]
style state fill:#f9f,stroke:#333,stroke-width:2px
この図から分かるように、状態が App コンポーネントに集中し、子コンポーネントへ Props として伝播しています。これにより、中間コンポーネントが不要な Props を受け取る「Props ドリリング」が発生します。
Jotai が目指す設計哲学
Jotai は、状態をアトム(atom)という最小単位に分割し、コンポーネントから完全に独立させることで、この問題を解決します。
Jotai の設計哲学は以下の 3 つの原則に基づいています:
| # | 原則 | 説明 |
|---|---|---|
| 1 | アトミック設計 | 状態を最小単位(atom)に分割し、必要な箇所でのみ使用 |
| 2 | ボトムアップ構成 | 小さな atom を組み合わせて複雑な状態を構築 |
| 3 | リアクティブ依存 | atom 間の依存関係が自動的に追跡され、変更が伝播 |
この設計により、状態とコンポーネントが疎結合になり、それぞれが独立して進化できるようになります。
課題
コンポーネントと状態の密結合問題
従来の React アプリケーションでは、コンポーネントと状態が密結合することで、以下のような具体的な課題が発生します。
課題 1:状態の再利用性の低下
コンポーネント内で定義された状態は、そのコンポーネントでしか使用できません。同じ状態を別のコンポーネントで使いたい場合、状態を親コンポーネントに移動させる必要があります。
typescript// ❌ 悪い例:コンポーネントに状態が密結合
function UserProfile() {
// この状態は UserProfile でしか使えない
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
fetchUser();
}, []);
// ...
}
課題 2:テストの複雑化
状態がコンポーネント内にある場合、状態のロジックをテストするためにコンポーネント全体をレンダリングする必要があります。
typescript// ❌ テストが複雑:UI とロジックが分離できない
test('ユーザー情報の取得', async () => {
// コンポーネントをレンダリングしないとテストできない
const { getByText } = render(<UserProfile />);
await waitFor(() => {
expect(getByText(/ユーザー名/)).toBeInTheDocument();
});
});
課題 3:状態の依存関係の管理
複数の状態が相互に依存する場合、useEffect を使って手動で同期を取る必要があり、コードが複雑になります。
typescript// ❌ 悪い例:状態間の依存を手動管理
function ProductList() {
const [products, setProducts] = useState([]);
const [filter, setFilter] = useState('all');
const [filteredProducts, setFilteredProducts] = useState(
[]
);
// 依存関係を手動で管理
useEffect(() => {
const filtered = products.filter((p) => {
if (filter === 'all') return true;
return p.category === filter;
});
setFilteredProducts(filtered);
}, [products, filter]); // 依存配列の管理が必要
// ...
}
以下の図は、状態間の依存関係を手動管理する場合の複雑さを示しています。
mermaidflowchart TD
products["products 状態"] -->|useEffect で監視| effect1["useEffect"]
filter["filter 状態"] -->|useEffect で監視| effect1
effect1 -->|手動で計算| filtered["filteredProducts 状態"]
products -->|useEffect で監視| effect2["useEffect(別の処理)"]
effect2 -->|手動で計算| count["productCount 状態"]
filtered -->|useEffect で監視| effect3["useEffect(更に別の処理)"]
count -->|useEffect で監視| effect3
effect3 -->|手動で計算| summary["summary 状態"]
style effect1 fill:#faa,stroke:#333
style effect2 fill:#faa,stroke:#333
style effect3 fill:#faa,stroke:#333
この図から、状態の依存関係が増えるにつれて useEffect が増殖し、管理が複雑になることが分かります。
グローバル状態管理の課題
Redux のようなグローバル状態管理ライブラリを使用すると、状態をコンポーネントから分離できますが、別の課題が生じます。
| # | 課題 | 詳細 |
|---|---|---|
| 1 | ボイラープレート | アクション、リデューサー、型定義など大量のコードが必要 |
| 2 | パフォーマンス | グローバル状態の一部が更新されると、関連するすべてのコンポーネントが再レンダリング |
| 3 | 学習コスト | 独自の概念(Store、Dispatch、Selector など)を学ぶ必要がある |
これらの課題に対して、Jotai はシンプルかつ効率的な解決策を提供します。
解決策
Jotai のアトミック設計
Jotai は、状態を「atom」という最小単位に分割し、コンポーネントから完全に独立させます。これにより、状態の再利用性とテスタビリティが大幅に向上します。
Atom による状態の定義
Jotai では、状態を atom として定義します。atom はコンポーネントの外部で定義され、どのコンポーネントからでもアクセスできます。
typescript// atoms/userAtoms.ts
import { atom } from 'jotai';
// ユーザー情報を保持する atom
export const userAtom = atom(null);
// ローディング状態を保持する atom
export const loadingAtom = atom(false);
// エラー状態を保持する atom
export const errorAtom = atom(null);
このように、状態をファイル単位で管理することで、状態の所在が明確になり、コンポーネントから分離されます。
コンポーネントでの atom 使用
定義した atom は、useAtom フックを使ってコンポーネント内で読み書きできます。
typescript// components/UserProfile.tsx
import { useAtom } from 'jotai';
import {
userAtom,
loadingAtom,
errorAtom,
} from '../atoms/userAtoms';
function UserProfile() {
// atom を使用(useState と同じインターフェース)
const [user, setUser] = useAtom(userAtom);
const [loading, setLoading] = useAtom(loadingAtom);
const [error, setError] = useAtom(errorAtom);
// コンポーネントはビューロジックに集中
if (loading) return <div>読み込み中...</div>;
if (error) return <div>エラー: {error}</div>;
if (!user) return <div>ユーザーが見つかりません</div>;
return <div>{user.name}</div>;
}
コンポーネントは状態の定義を持たず、atom を通じて状態にアクセスするだけです。これにより、コンポーネントの責務が明確になります。
派生状態(Derived Atoms)によるリアクティブな依存関係
Jotai の最も強力な機能の 1 つが、派生 atom(Derived Atoms)です。他の atom に依存する atom を定義することで、自動的にリアクティブな依存関係を構築できます。
読み取り専用の派生 atom
他の atom から計算された値を返す読み取り専用の atom を定義できます。
typescript// atoms/productAtoms.ts
import { atom } from 'jotai';
// 基本 atom
export const productsAtom = atom([]);
export const filterAtom = atom('all');
typescript// 派生 atom:フィルタリングされた商品リスト
export const filteredProductsAtom = atom((get) => {
const products = get(productsAtom); // productsAtom の値を取得
const filter = get(filterAtom); // filterAtom の値を取得
if (filter === 'all') return products;
return products.filter((p) => p.category === filter);
});
この派生 atom は、productsAtom または filterAtom が更新されると自動的に再計算されます。useEffect による手動管理は不要です。
読み書き可能な派生 atom
派生 atom は読み取りだけでなく、書き込みロジックも持つことができます。
typescript// atoms/counterAtoms.ts
import { atom } from 'jotai';
const countAtom = atom(0);
// 読み書き可能な派生 atom
export const doubleCountAtom = atom(
// 読み取り:count の 2 倍を返す
(get) => get(countAtom) * 2,
// 書き込み:元の count を更新
(get, set, newValue) => {
set(countAtom, newValue / 2);
}
);
この atom を使用すると、doubleCountAtom に値を設定すると自動的に countAtom が更新されます。
以下の図は、Jotai における atom 間のリアクティブな依存関係を示しています。
mermaidflowchart LR
products["productsAtom<br/>(基本 atom)"]
filter["filterAtom<br/>(基本 atom)"]
products -->|自動追跡| filtered["filteredProductsAtom<br/>(派生 atom)"]
filter -->|自動追跡| filtered
filtered -->|自動追跡| count["productCountAtom<br/>(派生 atom)"]
count -->|自動追跡| summary["summaryAtom<br/>(派生 atom)"]
filtered -->|自動追跡| summary
style products fill:#9cf,stroke:#333
style filter fill:#9cf,stroke:#333
style filtered fill:#9f9,stroke:#333
style count fill:#9f9,stroke:#333
style summary fill:#9f9,stroke:#333
この図から分かるように、基本 atom(青)が更新されると、それに依存する派生 atom(緑)が自動的に再計算されます。開発者は依存関係を手動で管理する必要がありません。
非同期処理の統合
Jotai は非同期処理もシームレスに統合できます。Promise を返す atom を定義するだけで、Suspense と組み合わせて使用できます。
非同期 atom の定義
typescript// atoms/userAtoms.ts
import { atom } from 'jotai';
const userIdAtom = atom(1);
// 非同期データを取得する atom
export const userAtom = atom(async (get) => {
const userId = get(userIdAtom);
// API からデータを取得
const response = await fetch(`/api/users/${userId}`);
if (!response.ok)
throw new Error('ユーザーの取得に失敗しました');
return response.json();
});
この atom は Promise を返すため、React の Suspense と組み合わせて使用できます。
Suspense との統合
typescript// components/UserProfile.tsx
import { Suspense } from 'react';
import { useAtomValue } from 'jotai';
import { userAtom } from '../atoms/userAtoms';
function UserProfile() {
// 非同期 atom を読み取り専用で使用
const user = useAtomValue(userAtom);
return <div>{user.name}</div>;
}
typescript// components/App.tsx
function App() {
return (
<Suspense fallback={<div>読み込み中...</div>}>
<UserProfile />
</Suspense>
);
}
userAtom が Promise を解決するまで、Suspense の fallback が表示されます。エラーハンドリングも Error Boundary で統一的に処理できます。
Atom の組織化パターン
状態をコンポーネントから分離すると、atom の組織化が重要になります。以下は推奨されるパターンです。
ドメイン別の atom 管理
typescript// atoms/user/
// - userAtoms.ts # ユーザー関連の atom
// - authAtoms.ts # 認証関連の atom
//
// atoms/product/
// - productAtoms.ts # 商品関連の atom
// - cartAtoms.ts # カート関連の atom
ドメインごとにファイルを分割することで、状態の所在が明確になり、チーム開発でも競合が減少します。
ファミリー atom によるパラメータ化
同じ構造の状態を複数管理する場合、atom ファミリーを使用します。
typescript// atoms/todoAtoms.ts
import { atomFamily } from 'jotai/utils';
// ID ごとに異なる todo を管理
export const todoAtomFamily = atomFamily((id: number) =>
atom({
id,
title: '',
completed: false,
})
);
typescript// components/TodoItem.tsx
function TodoItem({ id }: { id: number }) {
// ID に応じた atom を取得
const [todo, setTodo] = useAtom(todoAtomFamily(id));
return (
<div>
<input
value={todo.title}
onChange={(e) =>
setTodo({ ...todo, title: e.target.value })
}
/>
</div>
);
}
atom ファミリーにより、動的な数の状態を効率的に管理できます。
具体例
実践例:ショッピングカートの実装
Jotai を使ってショッピングカート機能を実装することで、リアクティブ思考法の実践的な活用方法を見ていきます。
Step 1:基本 atom の定義
まず、商品データとカート内のアイテムを管理する基本 atom を定義します。
typescript// atoms/shopAtoms.ts
import { atom } from 'jotai';
// 商品の型定義
interface Product {
id: number;
name: string;
price: number;
category: string;
}
// カートアイテムの型定義
interface CartItem {
productId: number;
quantity: number;
}
typescript// 全商品リスト(実際は API から取得)
export const productsAtom = atom<Product[]>([
{
id: 1,
name: 'ノート PC',
price: 120000,
category: 'electronics',
},
{
id: 2,
name: 'マウス',
price: 3000,
category: 'electronics',
},
{
id: 3,
name: '本棚',
price: 15000,
category: 'furniture',
},
]);
// カート内のアイテム
export const cartItemsAtom = atom<CartItem[]>([]);
これらの atom は状態の基礎となるデータを保持します。
Step 2:派生 atom でビジネスロジックを実装
次に、カートの合計金額や商品詳細を計算する派生 atom を定義します。
typescript// カート内の商品詳細(商品情報とカートアイテムを結合)
export const cartDetailsAtom = atom((get) => {
const products = get(productsAtom);
const cartItems = get(cartItemsAtom);
return cartItems.map((item) => {
// 商品情報を検索
const product = products.find(
(p) => p.id === item.productId
);
return {
...item,
product,
subtotal: product ? product.price * item.quantity : 0,
};
});
});
typescript// カートの合計金額を計算
export const cartTotalAtom = atom((get) => {
const cartDetails = get(cartDetailsAtom);
return cartDetails.reduce(
(total, item) => total + item.subtotal,
0
);
});
typescript// カート内の商品数を計算
export const cartItemCountAtom = atom((get) => {
const cartItems = get(cartItemsAtom);
return cartItems.reduce(
(count, item) => count + item.quantity,
0
);
});
これらの派生 atom は、cartItemsAtom や productsAtom が更新されると自動的に再計算されます。
以下の図は、ショッピングカートにおける atom 間の依存関係を示しています。
mermaidflowchart TD
products["productsAtom<br/>商品マスタ"]
cart["cartItemsAtom<br/>カート内容"]
products -->|結合| details["cartDetailsAtom<br/>商品詳細付きカート"]
cart -->|結合| details
details -->|集計| total["cartTotalAtom<br/>合計金額"]
cart -->|集計| count["cartItemCountAtom<br/>商品数"]
details -.->|表示| comp1["CartList コンポーネント"]
total -.->|表示| comp2["CartSummary コンポーネント"]
count -.->|表示| comp3["CartBadge コンポーネント"]
style products fill:#9cf,stroke:#333
style cart fill:#9cf,stroke:#333
style details fill:#9f9,stroke:#333
style total fill:#9f9,stroke:#333
style count fill:#9f9,stroke:#333
style comp1 fill:#ff9,stroke:#333
style comp2 fill:#ff9,stroke:#333
style comp3 fill:#ff9,stroke:#333
図の要点:
- 基本 atom(青)から派生 atom(緑)が自動計算される
- コンポーネント(黄)は必要な atom のみを購読する
- 状態の変更が依存する atom に自動伝播する
Step 3:カート操作の atom を実装
カートへの商品追加・削除などの操作を、書き込み可能な派生 atom として実装します。
typescript// カートに商品を追加する atom
export const addToCartAtom = atom(
null, // 読み取り不要
(get, set, productId: number) => {
const cartItems = get(cartItemsAtom);
// 既にカートにある場合は数量を増やす
const existingItem = cartItems.find(
(item) => item.productId === productId
);
if (existingItem) {
set(
cartItemsAtom,
cartItems.map((item) =>
item.productId === productId
? { ...item, quantity: item.quantity + 1 }
: item
)
);
} else {
// 新規追加
set(cartItemsAtom, [
...cartItems,
{ productId, quantity: 1 },
]);
}
}
);
typescript// カートから商品を削除する atom
export const removeFromCartAtom = atom(
null,
(get, set, productId: number) => {
const cartItems = get(cartItemsAtom);
set(
cartItemsAtom,
cartItems.filter(
(item) => item.productId !== productId
)
);
}
);
typescript// 商品の数量を更新する atom
export const updateQuantityAtom = atom(
null,
(
get,
set,
{
productId,
quantity,
}: { productId: number; quantity: number }
) => {
const cartItems = get(cartItemsAtom);
if (quantity <= 0) {
// 数量が 0 以下なら削除
set(
cartItemsAtom,
cartItems.filter(
(item) => item.productId !== productId
)
);
} else {
set(
cartItemsAtom,
cartItems.map((item) =>
item.productId === productId
? { ...item, quantity }
: item
)
);
}
}
);
これらの atom により、カート操作のロジックがコンポーネントから完全に分離されます。
Step 4:コンポーネントでの使用
定義した atom をコンポーネントで使用します。コンポーネントは UI の描画と atom の呼び出しに集中します。
typescript// components/ProductList.tsx
import { useAtomValue, useSetAtom } from 'jotai';
import {
productsAtom,
addToCartAtom,
} from '../atoms/shopAtoms';
function ProductList() {
const products = useAtomValue(productsAtom); // 読み取り専用
const addToCart = useSetAtom(addToCartAtom); // 書き込み専用
return (
<div>
{products.map((product) => (
<div key={product.id}>
<h3>{product.name}</h3>
<p>¥{product.price.toLocaleString()}</p>
<button onClick={() => addToCart(product.id)}>
カートに追加
</button>
</div>
))}
</div>
);
}
typescript// components/CartSummary.tsx
import { useAtomValue } from 'jotai';
import {
cartTotalAtom,
cartItemCountAtom,
} from '../atoms/shopAtoms';
function CartSummary() {
const total = useAtomValue(cartTotalAtom);
const itemCount = useAtomValue(cartItemCountAtom);
return (
<div>
<p>商品数: {itemCount} 点</p>
<p>合計金額: ¥{total.toLocaleString()}</p>
</div>
);
}
typescript// components/CartList.tsx
import { useAtomValue, useSetAtom } from 'jotai';
import {
cartDetailsAtom,
updateQuantityAtom,
removeFromCartAtom,
} from '../atoms/shopAtoms';
function CartList() {
const cartDetails = useAtomValue(cartDetailsAtom);
const updateQuantity = useSetAtom(updateQuantityAtom);
const removeFromCart = useSetAtom(removeFromCartAtom);
return (
<div>
{cartDetails.map((item) => (
<div key={item.productId}>
<h4>{item.product?.name}</h4>
<input
type='number'
value={item.quantity}
onChange={(e) =>
updateQuantity({
productId: item.productId,
quantity: parseInt(e.target.value) || 0,
})
}
/>
<p>小計: ¥{item.subtotal.toLocaleString()}</p>
<button
onClick={() => removeFromCart(item.productId)}
>
削除
</button>
</div>
))}
</div>
);
}
各コンポーネントは必要な atom のみを使用し、状態のロジックを一切持ちません。これにより、コンポーネントの責務が明確になり、テストも容易になります。
Step 5:状態のテスト
状態がコンポーネントから分離されているため、atom 単体でテストできます。
typescript// atoms/__tests__/shopAtoms.test.ts
import { describe, it, expect } from 'vitest';
import { createStore } from 'jotai';
import {
cartItemsAtom,
cartTotalAtom,
addToCartAtom,
productsAtom
} from '../shopAtoms';
describe('ショッピングカート atom', () => {
it('商品を追加すると合計金額が更新される', () => {
const store = createStore();
// 初期商品データを設定
store.set(productsAtom, [
{ id: 1, name: 'テスト商品', price: 1000, category: 'test' }
]);
// 商品を追加
store.set(addToCartAtom, 1);
// 合計金額を確認
const total = store.get(cartTotalAtom);
expect(total).toBe(1000);
});
typescript it('同じ商品を追加すると数量が増える', () => {
const store = createStore();
store.set(productsAtom, [
{ id: 1, name: 'テスト商品', price: 1000, category: 'test' }
]);
// 同じ商品を 2 回追加
store.set(addToCartAtom, 1);
store.set(addToCartAtom, 1);
const cartItems = store.get(cartItemsAtom);
expect(cartItems).toHaveLength(1);
expect(cartItems[0].quantity).toBe(2);
const total = store.get(cartTotalAtom);
expect(total).toBe(2000);
});
});
コンポーネントをレンダリングすることなく、状態のロジックを直接テストできます。これにより、テストの実行速度が向上し、テストコードもシンプルになります。
実装のメリット総括
この実装により、以下のメリットが得られます:
| # | メリット | 詳細 |
|---|---|---|
| 1 | 状態の再利用性 | atom は複数のコンポーネントから使用可能 |
| 2 | コンポーネントの簡潔性 | UI ロジックのみに集中でき、状態管理ロジックは atom に集約 |
| 3 | テスタビリティ | 状態を独立してテストでき、モックが不要 |
| 4 | パフォーマンス | 必要な atom のみを購読するため、不要な再レンダリングが発生しない |
| 5 | 保守性 | ビジネスロジックが atom に集約され、変更箇所が明確 |
まとめ
Jotai のリアクティブ思考法は、状態をコンポーネントから完全に切り離すことで、React アプリケーションの設計に新しいアプローチをもたらします。
この記事で解説した主要なポイントは以下の通りです:
設計哲学の本質
Jotai は、状態を最小単位(atom)に分割し、ボトムアップで組み合わせるアプローチを採用しています。これにより、状態とコンポーネントが疎結合になり、それぞれが独立して進化できるようになります。従来の Props ドリリングや useEffect による依存管理から解放され、より宣言的で直感的なコードが実現します。
リアクティブな依存関係
派生 atom を使用することで、atom 間の依存関係を自動的に追跡し、変更を伝播させることができます。手動で useEffect を記述する必要がなく、依存配列の管理ミスも発生しません。これにより、コードの可読性と保守性が大幅に向上するでしょう。
テスタビリティの向上
状態がコンポーネントから分離されているため、状態のロジックを独立してテストできます。コンポーネントのレンダリングやモックの作成が不要になり、テストコードがシンプルになります。これは、継続的なリファクタリングと品質保証に大きく貢献します。
実践的な活用
ショッピングカートの実装例で示したように、Jotai は実際のアプリケーション開発において、状態管理の複雑さを大幅に軽減します。基本 atom と派生 atom を組み合わせることで、複雑なビジネスロジックを宣言的に表現でき、コンポーネントは UI に集中できるようになります。
Jotai のリアクティブ思考法を採用することで、React アプリケーションはよりスケーラブルで保守しやすいものになるはずです。状態とコンポーネントの分離は、単なる技術的な改善ではなく、アプリケーション設計における根本的なパラダイムシフトと言えるでしょう。
ぜひ、あなたのプロジェクトでも Jotai を試してみてください。小さな atom から始めて、徐々にリアクティブな設計思想に慣れていくことをお勧めします。
関連リンク
articleJotai のリアクティブ思考法:コンポーネントから状態を切り離す設計哲学
articleJotai 運用ガイド:命名規約・debugLabel・依存グラフ可視化の標準化
articleJotai のスケール特性を実測:コンポ数 × 更新頻度 × 派生深さのベンチ
articleJotai が再レンダリング地獄に?依存グラフの暴走を止める診断手順
articleJotai ドメイン駆動設計:ユースケースと atom の境界を引く実践
articleJotai クイックリファレンス:atom/read/write/derived の書き方早見表
articlePrisma 読み書き分離設計:読み取りレプリカ/プロキシ/整合性モデルを整理
articleMermaid で日本語が潰れる問題を解決:フォント・エンコード・SVG 設定の勘所
articlePinia 2025 アップデート総まとめ:非互換ポイントと安全な移行チェックリスト
articleMCP サーバー 実装比較:Node.js/Python/Rust の速度・DX・コストをベンチマーク検証
articleLodash のツリーシェイクが効かない問題を解決:import 形態とバンドラ設定
articleOllama のインストール完全ガイド:macOS/Linux/Windows(WSL)対応手順
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来