- 概要
- 01. 過剰なProp Drilling
- 02. 過剰なimport
- 03. component内に過剰にビジネスロジックを記述する
- 04. 過剰なre-render
- 05. ナンニデモuseEffect
- 06. ナンニデモ &&
- 07. ナンニデモ三項演算子
- 08. propsをそのまま使用する
- 09. React.VC、React.VFCを使用する
- 10. 無名関数を使用したonXの汚染
- 11. インラインスタイルを使用する
- 12. import React from 'react'; を使用する
- まとめ
- 参考記事
概要
みなさんこんにちは。フルスタックエンジニアの高瀬 @takasehiromichi です。
Reactは、記述の自由度が高い一方で、Bad Practiceとされるものがあり、「よくない」書き方が存在します。
エンジニアによって、Bad Practiceを把握している人と、そうでない人がいるため、Bad Practiceについて記事にしようと思います。
01. 過剰なProp Drilling
Reactはpropsを子componentに渡すことができますが、技術的には無限にpropsを渡し続けることができます。 (親 > 親の子 > 親の子の子 > 親の子の子の子 ...)
しかし、過剰なprop drillingは、様々な苦痛を伴います。
stateが更新された時、propsが更新された時、親componentがre-renderされた時に、componentはre-renderされます。
そのため、過剰なprop drillingはre-renderの問題を引き起こす可能性があります。
また、過剰なprop drillingは可読性を低下させます。
そのため、propsを渡す数はなるべく少なくなるよう努力した方がいいでしょう。
個人的には、useContextの使用をおすすめします。また、componentは、親、子、孫までとし、それ以上になりそうな時は、コードの構成を見直します。
02. 過剰なimport
例えば、Material UIのButtonを使用したい場合に、
import * as MUI from '@material-ui/core'; <MUI.Button>aaa</MUI.Button>
と、書くことができます。
しかし、この記述はcoreの全てをロードするため、非常に冗長です。
そのため、本当に必要なものだけロードできるように記述するのが望ましいです。
import { Button } from '@material-ui/core'; <Button>aaa</Button>
03. component内に過剰にビジネスロジックを記述する
component内にビジネスロジックが大量に記述されていると、可読性が低下します。
componentとしての粒度が適切か、useReducer、useContextで代替可能か、ビジネスロジックをutilとして外出しできないかを検討すべきです。
component内のビジネスロジックは必要最低限とし、あくまでUIを描画することをメインとした方がいいでしょう。
04. 過剰なre-render
componentとして切り分けていると、過剰なre-renderが発生することがよくあります。
useMemo、useCallbackを適切に使用して、過剰なre-renderを抑制しましょう。
05. ナンニデモuseEffect
componentがrenderされたときに処理したいからと言って、ナンニデモuseEffectを使用すればいいということではありません。
Reactが推奨するuseEffectの使用方法は以下リンク先にありますので、参考にした方がいいでしょう。
いくつか抜粋します。
- componentが表示されている間、ネットワーク、API、ライブラリと接続したままにしておき、その後、接続を解除する必要がある
- setInterval()とclearInterval()
- windows.addEventListener()とwindow.removeEventListener()
- animation.start()とanimation.reset()
- カスタムフックで利用
- 非 React ウィジェットの制御
- fetch
06. ナンニデモ &&
&& は便利ですが、反面、意図しない挙動になる可能性があります。
例えば、以下のコードは0を表示します。
const amount = 0; return <span>{amount && `現在の数量は ${amount} です。`}</span>;
また、UI描画部分にビジネスロジックが混入しているため、好ましくありません。
以下のように、明示的にnullを返した方がいいでしょう。
const Component = ({ amount }: { amount: number }): JSX.Element | null => { if (amount) { return <span>{`現在の数量は ${amount} です。`}</span>; } else return null; }; const amount = 0; return <Component amount={amount} />;
07. ナンニデモ三項演算子
三項演算子は便利ですが、使い所を誤ると、可読性の低下を招きます。
例えば、以下はUI描画部分に三項演算子を使用した例です。
これはまだ読めるかもしれませんが、この記述の仕方が複数個、至る所で使用されていたらどうでしょうか。きっと読みにくいと思います。
ビジネスロジックは、関数かcomponentに切り出しましょう。
const Component = ({ isAdmin }: { isAdmin: boolean }): JSX.Element => { return ( <> <span>これが共通的な文章だとして</span> <div> {isAdmin ? ( <> <div>これはとてもadminなテキストですね</div> <div> <span>例えばこんな文が来て</span> <div> <span>こんな文が来たりとかして</span> </div> </div> </> ) : ( <> <div>これはadminではないですね</div> <span>例えばこんな文が来て</span> <div> <span>こんな文が来たりとかして</span> <span>こんな文も来たりとかして</span> </div> </> )} </div> <span>ここにfooterなんかが来たりして</span> </> ); }; const isAdmin = true; return <Component isAdmin={isAdmin} />;
以下は、関数にまとめてみた例です。
const renderAdminText = (isAdmin: boolean): JSX.Element | undefined => { let adminTextJSX = null; if (isAdmin) { adminTextJSX = ( <> <div>これはとてもadminなテキストですね</div> <div> <span>例えばこんな文が来て</span> <div> <span>こんな文が来たりとかして</span> </div> </div> </> ); } else { adminTextJSX = ( <> <div>これはadminではないですね</div> <span>例えばこんな文が来て</span> <div> <span>こんな文が来たりとかして</span> <span>こんな文も来たりとかして</span> </div> </> ); } return <div>{adminTextJSX}</div>; }; const Component = ({ isAdmin }: { isAdmin: boolean }): JSX.Element => { return ( <> <span>これが共通的な文章だとして</span> {renderAdminText(isAdmin)} <span>ここにfooterなんかが来たりして</span> </> ); }; const isAdmin = true; return <Component isAdmin={isAdmin} />;
08. propsをそのまま使用する
propsをpropsたるまま使用すると、propsがどんなプロパティを持つかわからないため、可読性の低下や、あらぬバグを孕む危険性があります。
const Component = (props: any) => { return ( <div> <h1>{props.title}</h1> <span>{props.text}</span> </div> ); }; return <Component title="タイトル" text="テキスト" />;
XPropsTypeの型を定義の上、分割代入引数としてプロパティを受け取りましょう。
type ComponentPropsType = { title: string; text: string; }; const Component = ({ title, text }: ComponentPropsType) => { return ( <div> <h1>{title}</h1> <span>{text}</span> </div> ); }; return <Component title="タイトル" text="テキスト" />;
09. React.VC、React.VFCを使用する
TypeScriptの文脈においては、React.VC、React.VFCを使用するメリットはありませんので、JSX.Elementを使用します。
※後述の参考記事を参照してください。
React.VCの使用
type ComponentPropsType = { title: string; text: string; }; const Component2: React.FC<ComponentPropsType> = ({ title, text, children, }) => { return ( <div> <span>{title}</span> <span>{text}</span> {children} </div> ); };
React.VFCの使用
type ComponentPropsTypeWithChildren = { title: string; text: string; children: JSX.Element; }; const Component3: React.VFC<ComponentPropsTypeWithChildren> = ({ title, text, children, }) => { return ( <div> <span>{title}</span> <span>{text}</span> {children} </div> ); };
JSX.Elementの使用
type ComponentPropsTypeWithChildren = { title: string; text: string; children: JSX.Element; }; const Component1 = ({ title, text, children, }: ComponentPropsTypeWithChildren): JSX.Element => { return ( <div> <span>{title}</span> <span>{text}</span> {children} </div> ); };
10. 無名関数を使用したonXの汚染
onX内で無名関数を使用するとUIだけでなくビジネスロジックが混入し、可読性が低下します。
const Component = () => { type eType = React.MouseEvent<HTMLButtonElement, MouseEvent>; const doSomething1 = (e: eType) => { console.log('doSomething1', e); }; const doSomething2 = (e: eType) => { console.log('doSomething2', e); }; return ( <button onClick={(e) => { doSomething1(e); doSomething2(e); }} > test </button> ); };
onXにはhandleXをあて、その中でビジネスロジックを記述します。
const Component = () => { type eType = React.MouseEvent<HTMLButtonElement, MouseEvent>; const doSomething1 = (e: eType) => { console.log('doSomething1', e); }; const doSomething2 = (e: eType) => { console.log('doSomething2', e); }; const handleOnClick = (e: eType) => { doSomething1(e); doSomething2(e); }; return <button onClick={handleOnClick}>test</button>; };
11. インラインスタイルを使用する
インラインスタイルは大きく可読性を低下させます。
基本的には、emotionを使用した方がいいですが、組織文化に合わせて選定するのがいいでしょう。
emotionなら、JSX Pragmaも不要で、以下だけで動きます。
tsconfig.json
{ "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "@emotion/react" } }
※後述の参考記事を参照してください。
12. import React from 'react'; を使用する
React 17からは、import React from 'react';
は不要です。
※後述の参考記事を参照してください。
まとめ
まだまだbad practiceはありそうですが、一旦ここまででまとめとしたいと思います。