最近 React の公式ドキュメント を読んでみたところ色々な気付きがありました。独学で React を学んでいると気付かないようなことも多いと思いますので、今回はそういったテクニックについてまとめてみたいと思います。
なるべくこの記事では簡潔に記載し、より詳しく知りたければリンク先を読んでもらう形にしたいと思います。
なお、基本的には最新版の React 18 で動作確認をしています。
React.Fragment
コンポーネントは JSX.Element を返す必要がありますが、React.Fragment を使うことにより複数の要素をつなげて返すことができます。
return (
<>
<h1>My Profile</h1>
<p>I am an office worker.</p>
</>
);
上記のコードは以下のコードの省略形です。
return (
<React.Fragment>
<h1>My Profile</h1>
<p>I am an office worker.</p>
</React.Fragment>
);
ループ中で使う場合は React.Fragment に key 属性を指定します。
{products.map((product) =>
<React.Fragment key={product.id}>
<h2>{product.name}</h2>
<p>{product.description}</p>
</React.Fragment>
}
論理 && 演算子による条件付きレンダー
JSX では && 演算子を使用して以下のような書き方ができます。
{ condition && expression }
たとえば今まで三項演算子で書いていたものを以下のように書けます。
{notifications.length >= 0
- ? <span class="badge">{notifications.length}</span>
- : <></>
+ && <span class="badge">{notifications.length}</span>
}
これは、JSX 中に書かれた式が false を返すと、React は何もレンダリングしないからです。他に true, null, undefined が返された場合も、何もレンダリングされません。
TypeScript と eslint を使用している場合は、strict-boolean-expressions を有効にするのが良いそうです。
event.target.name
React ではフォームの値が更新された場合に以下のようなイベントハンドラを書くことがあります。以下のサンプルコードでは、name と email を指定するテキストボックスがあり、それぞれの値が更新された場合にこれらの関数が呼ばれることを想定しています。
const [name, setName] = useState('');
const [email, setEmail] = useState('');
function handleChangeName(event) {
setName(event.target.value);
}
function handleChangeEmail(event) {
setEmail(event.target.value);
}
event.target.name を使うと、イベントハンドラをひとつにまとめることができるかもしれません。
const [formData, setFormData] = useState({ name: '', email: '' });
function handleChangeText(event) {
const newFormData = {...formData};
newFormData[event.target.name] = event.target.value;
setFormData(newFormData);
}
なお、公式ドキュメントでは、Computed property names を使用して書いていました。上の例を Computed property names を使用して書き換えると以下のようになるでしょう。
function handleChangeText(event) {
setFormData({...formData, [event.target.name]: event.target.value});
}
select タグと textarea タグの value 属性
select タグと textarea に、HTML には本来存在しないはずの value 属性が追加されています。
また、input[type="checkbox"]
と input[type="radio"]
のタグにも checked 属性が指定できるようになっています。
これにより、冗長なコードを省き、制御されたコンポーネントの管理を簡単に行うことが出来ます。
<select value={selectedItem}>
<option value="strawberry">イチゴ</option>
<option value="mint">ミント</option>
<option value="chocolate">チョコレート</option>
</select>
<textarea value={myVeryLongText} />
<input type="checkbox" checked={isChecked} />チェックボックス
props.children
React では children という特別な props を使うことができます。 タグで囲まれた子要素は props.children
として受け取ることができ、コンポーネントの内容として自由に配置することができます。
<FancyBorderComponent>
<h1>Hello World</h1>
Welcome to React!
</FancyBorderComponent>
// コンテンツに派手なボーダーを追加する
function FancyBorderComponent(props) {
return (
<div class="fancy-border">
{props.children}
</div>
);
}
TypeScript を使う場合は、props.children は React.Node 型を指定するのが良いでしょう。
style の単位を省略すると px になる
CSS では 0 以外の数値の単位は省略できませんでしたが、Reactでは省略できます。
(なお、CSS プロパティによって、単位が自動で付く場合と付かない場合があります。string 型を指定しておくと間違いなく単位が付かないので、付けたくない場合はそれが確実だと思います。)
You can pass strings or numbers as values. If you pass a number, like width: 100, React will automatically append px (“pixels”) to the value unless it’s a unitless property.
<div style={{ height: 150, padding: 50, opacity: 0.5, backgroundColor: 'red' }}>
Hello World!
</div>
<div className="hello-world">
Hello World!
</div>
.hello-world {
height: 150px;
padding: 50px;
opacity: 0.5;
background-color: blue;
}
カスタムプロパティでは自動で単位が付いてしまうという話もあるようですが、自分の環境では再現できませんでした。
カスタムフック
useState
や useEffect
などの hooks を使用する処理を別の関数に共通化することができます。これはカスタムフックと呼ばれますが、内部的にはただの関数です。
function useCount() {
const [count, setCount] = useState(0);
const countUp = () => {
setCount(count + 1);
}
const countMessage = `現在のカウントは ${count} です。`
return [countMessage, countUp];
}
これらは他のフックと同じように関数コンポーネントから呼ばれます。
function MyComponent() {
const [countMessage, countUp] = useCount();
return (
<>
{ countMessage }
<button onClick={countUp}>カウント</button>
</>
);
}
React を始めたばかりの頃は必要性を感じないかもしれませんが、いつか必要になったときに思い出してみてください。
useState の setFoo() に関数を渡せる
const [count, setCount] = useState(0);
var handleClick = () => {
setCount((c) => c + 1);
}
setFoo()
の引数として、新しい state を渡す代わりに、古い state を受け取って新しい state を返す関数 を渡すことができるようです。最新の state を使うことが出来るので、連続して setFoo() を呼び出すこともできます。
var handleClick = () => {
setCount((c) => c + 1);
setCount((c) => c + 1); // count の値が 2 増加する
}
var handleClickAlt = () => {
setCount(count + 1);
setCount(count + 1); // count の値は 1 しか増加しない
}
ただ、これを使わないといけないという場面はあまりないかも、と感じました。
Context
Reactアプリケーション全体で使用されるようなグローバルな変数を作成することができます。たとえばロケール設定やUIテーマ設定などです。詳細はリンク先を確認してください。
Portal
ポータルを使うと、DOM 上で異なる位置に、子コンポーネントをレンダリングすることができます。たとえば、overflow: hidden
が指定されているコンポーネント内でモーダルダイアログを表示したい場合などに使用できるそうです。
まとめ
React の公式ドキュメントを読み、知らないことが多くて面白かったです。日本語だったのでこちらのドキュメントを読みましたが、少し古いようだったので、まだ最新のReactには知らない機能がたくさんあるかもしれません。逆に関数コンポーネントではあまり使われない機能の解説もあり、新鮮に感じました。これに関連して、この記事の内容と直接は関係ありませんが、フォロワーさんから頂いた興味深い記事のリンクを貼っておきます。React Hooks が発表された当時の様子が色々と書かれています。
もし何か間違いなどあればコメントお願いします!