先日、ペアプロしている際に、if文を書く位置をどうするかに、そこにif文を書くのではなく、別の書き方をしたほうが可読性が高いよという話をしていたので、if文を書く位置について普段考えていることをまとめます。
まず、自分が考えているのは、基本的に以下の2点です。reactで具体例を書いていきます。
- 可読性・テスタビリティ高くため、if文によって処理が分岐する箇所を最低限の場所になるようにする
- if文の中に処理は基本的に書かずに、分岐後の処理の詳細はメソッドに切り出す
if文の場所が複数箇所にあり、可読性が低いbad例
export const Bad: React.FC<{ isAdmin: boolean }> = ({ isAdmin }) => {
return (
<div>
{isAdmin && <p>adminにはこれを見せるよ</p>}
{!isAdmin && <p>userにはこっち見せるよ</p>}
<p>両方に見せるよ</p>
{isAdmin && <p>adminだけこの情報も見せるよ</p>}
</div>
)
}
- adminなのか、userなのかロールによる条件分岐をずっと気にしなければ、ならないため可読性が低い
if文がまとまっており、可読性が高いbetter例
こちらの例ではadminなのか、userなのかロールによる条件分岐を気にするタイミングが、最初だけであるため、気にするタイミングが少なく可読性が高いです。
export const Better: React.FC<{ isAdmin: boolean }> = ({ isAdmin }) => {
if (isAdmin) {
return (
<>
<p>adminにはこれを見せるよ</p>
<p>両方に見せるよ</p>
<p>adminだけこの情報も見せるよ</p>
</>
)
}
return (
<>
<p>userにはこっち見せるよ</p>
<p>両方に見せるよ</p>
</>
)
}
出し分けロジック、表示内容の詳細で責務が別れており、拡張性が高いgood例
better例と基本的には同じですが、別コンポネに表示内容の詳細は切り出すことで、拡張性が高くなります。
また、AdminComponent
単体でテストをしたり、storybook
を作ったりしやすくもなります。
const AdminComponent = () => {
return (
<>
<p>adminにはこれを見せるよ</p>
<p>両方に見せるよ</p>
<p>adminだけこの情報も見せるよ</p>
</>
)
}
const UserComponent = () => {
return (
<>
<p>userにはこっち見せるよ</p>
<p>両方に見せるよ</p>
</>
)
}
export const Good: React.FC<{ isAdmin: boolean }> = ({ isAdmin }) => {
if (isAdmin) {
return <AdminComponent />
}
return <UserComponent />
}
- Goodコンポーネントが持つ責務が、出し分けだけになるため、表示の複雑度が上がった際に対応しやすく、各種表示のロジックの詳細を別コンポーネントに分離することができる
if文が一箇所にまとまるため、テストがしやすい
bad例でのtestを書くと例えばこのようになり、「adminにはこれを見せるよ」と「adminだけこの情報も見せるよ」の2つが表示されており、「userにはこっち見せるよ」が表示されてないことをテストする必要があります。
it('Bad admin', () => {
const { container, asFragment } = render(<Bad isAdmin />)
expect(container.textContent).toContain('adminにはこれを見せるよ')
expect(container.textContent).toContain('adminだけこの情報も見せるよ')
expect(container.textContent).not.toContain('userにはこっち見せるよ')
expect(asFragment()).toMatchSnapshot()
})
betterでのtestでは、「adminにはこれを見せるよ」が表示されており、「userにはこっち見せるよ」が表示されていないことをテストできれば、表示の切り分けが十分にテストできてるので、テストが簡単になります。
goodのコンポーネントも分けた状態も同様です。
また、場合によっては、good例の場合は、個別のコンポネはshallow mount(この例では、react testing libraryを使用してるためshallow mountはありませんが)したり、mockしたほうがテストは簡単にかけるかもしれません。
it('Better admin', () => {
const { container, asFragment } = render(<Better isAdmin />)
expect(container.textContent).toContain('adminにはこれを見せるよ')
expect(container.textContent).not.toContain('userにはこっち見せるよ')
expect(asFragment()).toMatchSnapshot()
})
今回は、reactを例に書いていますが、vueはもちろん、サーバーサイドの処理でも同様の考え方でif文の処理が分岐していくようにすると可読性が高く・テスタブルな処理がかけます。
条件分岐が複雑になりそうな処理を書く際は、この2つを意識してみてください。
- 可読性・テスタビリティ高くため、if文によって処理が分岐する箇所を最低限の場所になるようにする
- if文の中に処理は基本的に書かずに、分岐後の処理の詳細はメソッドに切り出す
また、例に使用しているコードの全体はこちらにも上げています。
https://github.com/YasushiKobayashi/samples/blob/master/src/react-if-sample/src/Main.spec.tsx
https://github.com/YasushiKobayashi/samples/blob/master/src/react-if-sample/src/Main.tsx