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

フロントエンドで 良いコードを書くために

てけし
January 18, 2023

フロントエンドで 良いコードを書くために

フロントエンドLT新年会の資料
https://thecoo.connpass.com/event/269188/

てけし

January 18, 2023
Tweet

Other Decks in Programming

Transcript

  1. フロントエンドで
    良いコードを書くために
    Sansan株式会社
    技術本部 Strategic Products Engineering Unit
    Contract One Devグループ
    井上丈士

    View Slide

  2. 写真が入ります
    井上丈士
    Sansan株式会社
    技術本部 Strategic Products Engineering Unit
    Contract One Devグループ
    zenn: t-keshi
    twitter: t__keshi
    趣味: サウナ駆動技術記事投稿

    View Slide

  3. (我々は常に思い悩む)
    どうしたら良いコードが
    書けるようになるだろうか...?🤔

    View Slide

  4. 知識や経験...?🤔
    いや、もう少し具体化しないと
    コードを良くする方法が見えてこない

    View Slide

  5. 良いコードを書くための方法を、
    以下のように分類・整理することはできないだろうか?
    そこで、、、
    設計原則
    フレーム
    ワークの知識
    対話

    View Slide

  6. ①狭義の設計原則 設計原則

    View Slide

  7. 設計原則と聞いて、まず頭に浮かぶもののこと。
    オニオンアーキテクチャや
    クリーンアーキテクチャのようなものが挙げられる。
    「フロントエンドのクリーンアーキテクチャ」
    一時期、流行った、バックエンドのアーキテクチャを輸入する方針
    狭義の設計原則

    View Slide

  8. 設計原則の輸入の問題点

    View Slide

  9. オニオンインオニオンを良しとする意見もある。
    しかし、0コストで変更容易性が得られるわけではない。
    可読性・複雑さとのトレードオフ。
    その割に、フロントエンドはフレームワークへの依存が大きく、
    どう足掻いても変更はそれほど容易にならない。
    オニオンインオニオンの何が駄目か?

    View Slide

  10. 実装パターンとその背後にある考え方は、区別した方が良さそう。
    実装パターンとその背後にある考え方
    背後にある考え方
    BE実装パターン FE実装パターン

    View Slide

  11. 例えば責務を意識したレイヤー分け
    ❌悪い例
    データ取得の責務
    getUsers()などに切り出すべき
    表示の責務
    その背後にある考え方?
    const UsersPage = () => {
    const [user, setUsers] = useState(null)
    useEffect(() => {
    const asyncFn = async () => {
    const response = axios.get("https://hoge-api/users", {
    headers: {...省略...}
    })
    return response.data
    }
    asyncFn()
    }, [])
    return
    }

    View Slide

  12. いわゆるリーダブルコード的なもの。
    設計原則に含めていいのか微妙だが、
    設計本には、こうした内容も書いてある。
    ex.)良いコード悪いコードで学ぶ設計入門
    基本的には本の内容をFEにもそのまま適用可能。
    JavaScript/TypeScriptでリーダブルなコードを考えるなら、
    どんなことが言えるだろうか?
    広義の設計原則

    View Slide

  13. 静的に分岐漏れを検出する。
    条件分岐のコツ1
    TypeScript 4.9以降
    type Action =
    | { type: "OPEN"; payload: { message: string } }
    | { type: "CLOSE" }
    | { type: "TOGGLE" }
    const reducer = (action: Action) => {
    switch (action.type) {
    case "OPEN":
    return { isOpen: true, message: action.payload.message }
    case "CLOSE":
    return { isOpen: false, message: null }
    default: // TOGGLEがないのでコンパイルエラー
    throw new Error(action satisfies never)
    }
    }
    TypeScript 4.9以前
    type Action =
    | { type: "OPEN"; payload: { message: string } }
    | { type: "CLOSE" }
    | { type: "TOGGLE" }
    const reducer = (action: Action) => {
    switch (action.type) {
    case "OPEN":
    return { isOpen: true, message: action.payload.message }
    case "CLOSE":
    return { isOpen: false, message: null }
    default: // TOGGLEがないのでコンパイルエラー
    throw new Error((action as { type: "__invalid__" }).type)
    }
    }

    View Slide

  14. 処理の分岐というより、単純な対応を表す場合は、
    Switchの代わりにObjectを使う。
    条件分岐のコツ2
    type Status = "active" | "inactive" | "sleep"
    const UserStatus = (status: Status) => {
    const icon = {
    active: ,
    inactive: ,
    sleep:
    }[status]
    return {icon}
    }

    View Slide

  15. RoRoとは、”Receive an Object, Return an Object”
    オブジェクトで受けてオブジェクトで返すこと
    RoRo
    const createUser = (userId: string, userAccountId: string) => {
    // 省略
    }
    const UserRegistrationDialog = () => {
    const handleSubmit = (userAccountId: string, userId: string) => {
      // 引数を渡し間違える
      createUser(userAccountId, userId)
    }
    return
    }
    Objectを使うと混乱は防げる
    (他の言語でいう名前付き引数やキーワード引数)
    const handleSubmit = ({userId, userAccountId})
    => {
    createUser({ userId, userAccountId})
    }

    View Slide

  16. Gap

    View Slide

  17. ②フレームワークの知識

    View Slide

  18. あまりにもuseEffectが乱用されすぎている。
    これはありがちな悲劇。
    useEffectはエスケープハッチ、仕方なく使うもの。
    useMemoなどで済むようなケースで、
    安易にuseEffectを使ってはいけない。
    例えばReactだと1

    View Slide

  19. 余計なFragmentが多すぎる。
    些細な問題だが、多発すると可読性を下げる要因に。
    nullもstringもnumberも
    立派なReactElementであり、FCのReturnType。
    例えばReactだと2

    View Slide

  20. 無闇にStateに入れまくる。
    状態管理はシンプルに保つのがキモ。
    ❌悪い例
    例えばReactだと3
    const HogeComponent = () => {
    const [users, setUsers] = useState()
    const [admin, setAdmin] = useState()
    // その他数多くのStateなど, 500行くらい
    return // ようやくJSX
    }

    View Slide

  21. (再掲)フロントエンドにはフロントエンドの実装パターンがある。
    では、フロントエンドの実装パターンとは何だろう?
    それは、ある程度、フレームワーク依存になってしまう。
    Reactの場合だと、

    なのではないかと思う。
    フレームワークの実装パターン

    View Slide

  22. APIの呼び出しはsetLoadingやsetErrorなどのボイラプレートに溢れる
    これは特にCustomHookを使うべきところ
    react-useのCustomHookを参考にするのもおすすめ
    込み入ったロジックはどんどんCustomHookに切り出していこう
    CustomHook
    const HogeComponent = () => {
    const [state, setState] = useState()
    …500行くらいあるコード...
    return ようやくJSX
    }
    CustomHookで切り出そう

    View Slide

  23. taroさんのLTに続く
    コンポーネント設計

    View Slide

  24. Last
    Journey

    View Slide

  25. ③対話

    View Slide

  26. コンポーネント、それが一番大事
    - 基盤となるUIコンポーネントが不足していると、開発速度が出ない
    - 良いコンポーネントは、開発のアクセル
      そして、そんなコンポーネントを作るために必要なのが、

    View Slide

  27. デザインチームとの対話

    View Slide

  28. ビジネスサイドとの対話
    - ビジネスサイド、開発サイドの相互理解
    - いつもそれがうまくいくとは限らないが、チーム全体としてのたしかな進歩につな
    がる

    View Slide

  29. まとめ

    View Slide

  30. 対話
    フレームワークの知識
    設計原則
    良いコード

    View Slide

  31. 「なぜ良いコードが書けないんだろう」
    悩んだときは、それを各要素に分解し、
    各個撃破していくことが大事。
    ぼんやりした問題を具体的な要素に分解することさえできれば、
    「悩む」のではなく、「考える」ことができる。
    今日はそんな話がしたかった。

    View Slide

  32. 採用情報はこちら
    https://media.sansan-engineering.com/
    積極採用中!!

    View Slide

  33. ありがとうございました!

    View Slide

  34. 狭義の設計原則
    WebフロントエンドでDDDを導入のメリットってあるでしょうか
    WEBフロントエンドにおけるソフトウェア設計の考察
    広義の設計原則
    Elegant Patterns in Modern JavaScript
    フレームワークの基礎知識
    You Might Not Need an Effect
    参考記事

    View Slide