1分でわかる:原因はなに?
複数タブのある編集画面などで、「メインタブで数値を入力した後、別のタブに切り替えてから画面全体の【保存】ボタンを押すと、自動計算が走らずにデータが壊れる」という現象が起きることがあります。
原因は、Reactの超基本特性である「画面に表示されていない(マウントされていない)コンポーネントのプログラムは完全に消滅(アンマウント)する」という挙動を見落としていることです。
自動計算ロジックを「メインタブ(子)」の中に書いてしまうと、別のタブに切り替えた瞬間にそのプログラム自体が消滅するため、計算が動かなくなってしまいます。
視覚的なイメージ
❌ 悪い設計(子コンポーネントにロジックがある)
メイン以外のタブを開いた瞬間、メインタブが消えるため、中の計算ロジックも一緒に消滅します。
[親コンポーネント(保存ボタン)]
│
├── ❌ [メインタブ(計算ロジック)] ── 別のタブを開くと消滅!
└── ◯ [サブタブ]
⭕ 正しい設計(親コンポーネントにロジックを移動:リフトアップ)
計算ロジックを「親」に持たせることで、どのタブを開いていても常に裏で正しく計算され、安全に保存できます。
[親コンポーネント(計算ロジック & 保存ボタン)] ── 常にここで計算!
│
├── ◯ [メインタブ(入力・表示だけ)]
└── ◯ [サブタブ]
コードで見る対策
❌ 修正前:子(タブ内部)で計算していた場合
別のタブに切り替えると、この useEffect(計算ロジック)自体が画面から消えて動きません。
// 子(メインタブコンポーネント)の中に書いてしまっていた
useEffect(() => {
setProfit(sellingPrice - cost);
}, [sellingPrice, cost]);
⭕ 修正後:親(画面全体)へ引っ越し(リフトアップ)
画面全体を管理する親に移動します。これでタブの切り替えに影響されず、常に最新のデータが計算されます。
// 親コンポーネント(Edit.tsx)へ引っ越し
useEffect(() => {
setProfit(prices.sellingPrice - prices.cost);
}, [prices]); // 価格が変わるたびに「常に」親側で自動計算!
まとめ:コンポーネント設計の鉄則
- 画面全体の「保存」ボタンが使うデータやロジックは、絶対に「親」に置く(Lifting State Up)。
- 子コンポーネント(各タブなど)は「入力と表示」だけに専念させる。
- さらに堅牢にするなら、お金の計算などの重要ロジックはフロント(React)側だけでなく、バックエンド(Laravelなど)側でも行うように設計する。