【React】 コンポーネントを受け取ってコンポーネントを作る関数
高階関数は、関数を受け取る関数や、関数を返す関数でした。高階コンポーネントでは、コンポーネントを受け取ってコンポーネントを返すものになります。
高階関数の定義に当てはめると、コンポーネントを受け取ってそれ以外を返す関数も高階コンポーネントと言えそうですが、実質意味がありません。
高階関数をまだ良く知らない場合は、それの知識を先に得たほうがスムーズに理解できると思います。
高階コンポーネントの小さい例
まずは小さいサンプル的な実装例を見てみます。
import highOrderComponent from './components/highOrderComponent'; import MyComponent from './components/MyComponent'; export default function App() { // 高階コンポーネントを使用 const WrappedComponent = highOrderComponent(MyComponent); return ( <> <WrappedComponent /> </> ); }
// コンポーネントを受け取って、コンポーネントを返す関数 export default function highOrderComponent(WrappedComponent) { return () => ( <> <p>HOC</p> <WrappedComponent /> </> ); };
export default function MyComponent() { return ( <p>MyComponent</p> ) }
この例を見ると、highOrderComponent
という関数は、コンポーネントを引数で受け取り、
() => ( <> <p>HOC</p> <WrappedComponent /> </> )
というコンポーネントを返していることがわかります。
コンポーネントはPascalCase、関数名はcamelCaseという使い分けになっています。例えば
export default function highOrderComponent(wrappedComponent) { return () => ( <> <wrappedComponent /> </> ); };
のようにコンポーネント名をcamelCaseにすることはできません。Reactの仕様上、PascalCaseである必要があります。
実際の使用例
サンプル例で書き方を確認できたので、実際の例を見てみましょう。例えば、オンラインステータスを表示するコンポーネントを考えてみます。
ユーザのオンラインステータスは、例えばサイドのミニアイコンに表示されることもあれば、1対1のチャット画面の相手情報表示、フレンド一覧画面など、様々な場所で共通して利用されます。 では、それぞれのコンポーネントでステータスを埋め込むコードを毎回書くのでしょうか。それは非効率です。
そこで、高階コンポーネントを使います。
アイコン部分とステータス表示をセットにしたコンポーネントを作って、アバターの大きさの種類などをpropで渡せばいいじゃんと言われればそうなのですが、例ということで…
まずはアバターの情報を表示する部分 (オンラインステータス以外) を作ります。
export default function MiniAvatar(props) { return ( <> <img src={`https://.../public/avatars/${props.img}`} alt={`${props.name}'s avatar`} /> </> ) }
export default function MiniAvatar(props) { return ( <> <img src={`https://.../public/avatars/${props.img}`} alt={`${props.name}'s avatar`} /> <p>{props.name}</p> </> ) }
次にこれらのコンポーネントを表示するコンポーネントを作って返す関数を作成します。props
も渡せるようにしましょう。
export default function withOnlineStatus(WrappedComponent, user) { return () => { const onlineStatus = fetchIsOnline(user.id); // 実装は略 return ( <> <div> <WrappedComponent img={user.img} name={user.name} /> <p>{onlineStatus ? "Online" : "Offline"}</p> </div> </> ); }; };
これで、コンポーネントを作成する関数ができました。WrappedComponent
に入ってくるコンポーネントが、新しく作成されるコンポーネントに埋め込まれていることがわかります。また、引数で受け取った値をpropsとして渡しています。
次にこの関数を呼び出します。
import withOnlineStatus from './components/withOnlineStatus'; import MiniAvatar from './components/chat/MiniAvatar'; import UserListAvatar from './components/chat/UserListAvatar'; export default function App() { const [users] = useState([ { id: 1, name: "Jhon", img: "1.png" } ]); const MiniAvatarComponent = withOnlineStatus(MiniAvatar, users[0]); const UserListAvatarComponent = withOnlineStatus(UserListAvatar, users[0]); // ... }
withOnlineStatus
の第1引数にコンポーネントの関数本体を、第2引数に適当な値を入れています。そうして返ってきたコンポーネントを一旦変数に入れています。
後は表示するだけです。
<> <MiniAvatarComponent /> <UserListAvatarComponent /> </>
まとめ
高階コンポーネントは、コンポーネントを受け取ってコンポーネントを返す関数です。いくつかの種類のコンポーネント内に共通の処理を組み込みたい場合などに使用します。
処理をうまく分割して、使い回しがしやすくなるように工夫してみましょう。
