Upgrade to Pro — share decks privately, control downloads, hide ads and more …

「Reactを使って良かったこと」 を考えていく中で気づいたこと / insights-fro...

「Reactを使って良かったこと」 を考えていく中で気づいたこと / insights-from-reflecting-on-react

Avatar for コドモン開発チーム

コドモン開発チーム

February 12, 2026
Tweet

More Decks by コドモン開発チーム

Transcript

  1. 5

  2. 11 CONFIDENTIAL - © 2022 CoDMON Inc. 11 React選定時点のフロントエンド経験 •

    元々はVue2のみの経験 (少しだけComposition API) • 新規サービス立ち上げでReactを選定 • 同時期にVue2のコードも並行で触っていた  
  3. 12 CONFIDENTIAL - © 2022 CoDMON Inc. 12 Reactを触ってみての最初の感触 「状態管理がミスしにくい」

    •  値が意図しないものに変わることがない •  更新したい状態は確実に変わってくれる
  4. 13 CONFIDENTIAL - © 2022 CoDMON Inc. 13 なぜReactは初心者でも状態管理でミスしにくいのか •

    useStateがあり、set関数が実装されている  const [state, setState] = useState(initialState) • 標準レベル(Docの1ページ目レベル)でこういった関数が用意されてい るので、こっちを通りやすい分ミスしにくい reactでも state = “new value” と書けてしまうことは書けてしまう。  
  5. 14 CONFIDENTIAL - © 2022 CoDMON Inc. 14 Vue2初心者のころ export

    default { data() { return { items: ['a', 'b', 'c'] } }, methods: { updateItem() { this.items[0] = 'new value' // ❌検知されない } } }
  6. 19 CONFIDENTIAL - © 2022 CoDMON Inc. 19 Reactで良かった点 •

    TypeSprictとの相性が良い ジェネリックコンポーネントが書ける。 • 共通化と抽象化の柔軟性の高いコンポーネント合成ができる。 render propsパターンが良い感じ かつ型安全
  7. 20 CONFIDENTIAL - © 2022 CoDMON Inc. 20 ケース1:ジェネリックコンポーネント~select box~

    type SelectProps<T extends string> = { options: T[] value: T onChange: (value: T) => void } const Select = <T extends string>({ options, value, onChange }: SelectProps<T>) => { return ( <select value={value} onChange={(e) => onChange(e.target.value as T)}> {options.map((option) => ( <option key={option} value={option}>{option}</option> ))} </select> ) } Tの型で縛っている。
  8. 21 CONFIDENTIAL - © 2022 CoDMON Inc. 21 ケース1:ジェネリックコンポーネント~select box

    使う側 type Fruit = 'apple' | 'banana' | 'orange' const [fruit, setFruit] = useState<Fruit>('apple') <Select<Fruit> options={['apple', 'banana', 'orange']} value={fruit} onChange={setFruit} /> setFruit('peach') setFruitの方は(fruit: Fruit) => voidの必要がある peachはFruit型に属していないのでコンパイルエラー
  9. 22 CONFIDENTIAL - © 2022 CoDMON Inc. 22 ケース2:Render Props

    ~共通フォーム - 共通化用のフォームコンポーネントを作成する時 - 入力状態とバリデーションは共通化 - Submitボタンは抽象化 - UIとSubmit処理は使う側で決める
  10. 23 CONFIDENTIAL - © 2022 CoDMON Inc. 23 ケース2:Render Props

    ~共通フォーム type Props = { submitButton: (props: { onSubmit: (callback: (data: { name: string; hobby: string }) => void) => void }) => React.ReactNode } const ProfileForm = ({ submitButton }: Props) => { const [name, setName] = useState('') const [hobby, setHobby] = useState('') const handleFormSubmit = (callback: (data: { name: string; hobby: string }) => void) => { if (!validate()) return // バリデーション発火 callback({ name, hobby }) } return ( <div> <input value={name} onChange={e => setName(e.target.value)} placeholder="名前" /> <input value={hobby} onChange={e => setHobby(e.target.value)} placeholder="趣味" /> {submitButton({ onSubmit: handleFormSubmit })} </div> ) } 具体的に共通化している: - フォームの入力状態 - バリデーションロジックと発火タイミ ング - パーツの位置関係 抽象定義しておき、使う側で注入する: - submitのUI - バリデーション後の処理 submitButtonはReact.ReactNodeで受 け取るのではなく 「() => React.ReactNode」レンダーに する。
  11. 24 CONFIDENTIAL - © 2022 CoDMON Inc. 24 ケース2:Render Props

    ~ 使う側1 <ProfileForm submitButton={({ onSubmit }) => ( <button onClick={() => onSubmit((data) => { saveToServer(data) showToast('保存しました') })}> 送信 </button> )} /> ボタンのテキストは「送信」 バリデーションが通った後は サーバーに保存しトーストを表示 dataは { name: string; hobby: string }型
  12. 25 CONFIDENTIAL - © 2022 CoDMON Inc. 25 ケース2:Render Props

    ~ 使う側2 <ProfileForm submitButton={({ onSubmit }) => ( <button onClick={() => onSubmit((data) => { navigate('/confirm', { state: data }) })}> 次へ </button> )} /> ボタンのテキストは「次へ」 バリデーションが通った後は 確認画面へ遷移
  13. まとめ CoDMON P26 • 状態管理においてはインフラストラクチャはなんでも良い ◦ 言語、FWツールなんでも良い ◦ 設計やルールの整備が重要 ◦

    Reactは初心者が若干ハマりにくいかなレベル • TypeScriptをの親和性は良い ◦ ジェネリックコンポーネントがサクッと書ける ◦ コンポーネント設計の柔軟性と型安全を両立できる