next/link は atoms に含めるべきか?
- ① a タグとして機能する Component
- ② Link コンポーネントとして機能する Component
全く同じ見た目のこのふたつの Component がある場合、どうしますか?全く同じスタイルで ①② を両方作る方もいるかもしれませんが、私は ① しか作りません。理由は単純で、Link の子 Component として ① を使えば良いからです。
なぜ ② を作ってしまう?
理由は様々だと思いますが、おそらくこの様な理由ではないでしょうか。
- 都度 Link を import するのが面倒
- 型関連の都合で必要だから
- Link と分離するとエラーになるから(え?)
もし三番目が理由だとしたら、ここを読みましょう。
「React.forwardRef
を使え、ルーク(意訳)」
React.forwardRef ...?
ひと昔前は、React のスタイリングと言えば、CSS in JS がマジョリティでした。しかし今は、Tailwind や CSS Modules も選択肢として挙がり、拮抗している状況です。
これまで CSS in JS しか使ったことがない場合React.forwardRef
は初耳かもしれません。CSS in JS は暗黙的に ref forwarding を施しており、透過的に ref が挿ささります。どういうことかというと、このようなボタン定義があった場合。
const Button = styled.a`
color: #00f;
`;
Tailwind や CSS Modules で等価実装をしたい場合、次のものが必要です。CSS in JS と比べると一手間かかりますね。
const Button = React.forwardRef(({ className, ...props }, ref) => {
return <a {...props} className={clsx(className, styles.btn)} ref={ref} />;
});
.btn {
color: #00f;
}
CSS in JS の便利さに慣れてしまっていたら、なかなか気付きにくい点ではないでしょうか?ref forwarding が施されていない Function Component を Link の子にしてしまうと、この様なエラーがでます。
const Button: React.FC = ({ className, ...props }) => {
return <a {...props} className={clsx(className, styles.btn)} />;
});
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
atoms は ref forwarding しておくが吉
今回は next/link にフォーカスしましたが、input タグでも同様の議題はあります。react-hook-form などのフォームライブラリにおいては、ref forwarding が施されたコンポーネントの方が扱いやすく、ref forwarding が施されていないことが導入障壁になることがあります。
CSS in JS で styled.input
などを使われていたならば、これも同様に気づかない観点だったのではないでしょうか?CSS in JS の功罪は、暗黙的に ref forwarding していたことだと思います。React.forwardRef を知る機会は、CSS in JS を手放した時に訪れます。
Discussion