part-1 はこちら
プロップス(Props)
JSXタグに渡す情報のことをpropsと呼ぶ。HTMLの属性値に似たようなもの。
親コンポーネントは子コンポーネントにpropsを渡すことで情報を伝えることができる。
子コンポーネント側で使用したいpropsは引数で受け取る。
propsのデータの取り扱い
propsはあくまでも受け取ったデータのため、書き換えることはできない。
Reactの性質上、stateが更新された時に画面の再レンダリングが走る(単方向データフロー)ため、データを書き換えたい場合はstateを書き換えて、書き換えたデータをpropsに渡す必要がある。
// App()が持つcountとhandleClickをpropsとして各ボタンに渡す
export default function App() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>ボタンをクリックしてください</h1>
<MyButton count={count} onClick={handleClick} /> {/* Aボタン */}
<MyButton count={count} onClick={handleClick} /> {/* Bボタン */}
</div>
);
}
// 渡ってきたpropsを引数に渡すことでコンポーネント内で使用できるようにする
function MyButton({count, onClick}) {
return (
<button onClick={onClick}>
{count}回クリックされました
</button>
);
}
子コンポーネント側で受け取るpropsに値が渡されなかった場合に、デフォルト値を指定しておけばその値が使用される。
デフォルト値はsizeがない場合や値がundefinedが渡ってきた場合に適用されるため、nullや0を渡した場合にはデフォルト値は適用されない。
export default function App() {
const profileImage = 'xxx.jpg'
return (
<ProfileImage src={profileImage} />
)
}
function ProfileImage({src, size=100}) {
return (
<img src={src} alt="image" width={size}, height={size} />
)
}
スプレッド構文でpropsを渡す
propsの引き渡し数が多く、引数が長蛇になってしまう場合はスプレッド構文を使用して短く実装することができる。
profileProps = {
src: "xxx.jpg",
alt: "image",
width: 100,
height: 100
}
export default function App() {
return (
<ProfileImage {...profileProps} />
)
}
// 子コンポーネント側でもスプレッド構文で受け取ることができる
function ProfileImage(props) {
return (
<img {...props} />
)
}
■ コンポーネントごとに異なるpropsを渡したい場合
const images = {
'田中': {
src: "田中.jpg", alt: "田中さんのプロフィール画像", width: 100, height: 100
},
'山田': {
src: "山田.jpg", alt: "山田さんのプロフィール画像", width: 80, height: 80
}
}
function Profile({ name }) {
const imageInfo = images[name];
return (
<section>
<h2>{name}さんのプロフィール</h2>
<img src={imageInfo.src} alt={imageInfo.alt} width={imageInfo.width} height={imageInfo.height} />
</section>
);
}
export default function App() {
return (
<>
<Profile name="田中" />
<Profile name="山田" />
</>
);
}
■ props - childlen
JSXタグ内でコンポーネントをネストした場合、親側のコンポーネントはその中身をchildlenというpropsとして受け取る。
単調なレイアウト調整は共通のクラス名で再現できるが中身の構造が決まってないけど枠のレイアウトは共通にしたいときや、共通のJavaScriptの処理をしたいときなどに便利。
children
コンポーネント内の子要素を表す予約語。
children以外でコンポーネントの子要素を取得しようとしてもできないので注意。
function DoubleCheckWrap({ children }) {
const handleContainerClick = (e) => {
const isConfirmed = window.confirm("本当に実行してもよろしいですか?");
isConfirmed === false && e.stopPropagation(); // 中にあるボタンのonClickを実行させない
};
return (
<div onClickCapture={handleContainerClick}>
{children}
</div>
);
}
export default function App() {
return (
<>
<DoubleCheckWrap>
<button onClick={() => alert("データを削除しました")}>データを削除</button>
</DoubleCheckWrap>
<DoubleCheckWrap>
<button onClick={() => alert("設定をリセットしました")}>リセット</button>
</DoubleCheckWrap>
</>
);
}
実際にはUIライブラリから持ってきたボタンなどにJsの動きを追加したい場合にwrapで囲ってwrap内でオリジナルのjs実装をしたり、クリックした際の処理だけボタンのonClickの関数にもたせて確認処理が依存しないようにするために使用することができる。
イベントに応答する
コンポーネント内でイベントハンドラを宣言することで、イベントに応答できる。
export default function MyBotton() {
function handleClick() {
alert('クリックされました!');
}
return (
<Button onClick={handleClick}>クリックしてください</Button>
)
}
onClick={handleClick}の末尾に括弧をつけてしまうと、即時関数となってしまうので注意。
また、関数に引数を渡したい場合は、onClick={() => handleClick()}となる。
画面の更新
ボタンがクリックされた回数など、コンポーネントに情報を記憶させたい場合は、
コンポーネントにuseStateを追加するとstate変数を宣言できる。
import { useState } from 'react';
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
{count}回クリックされました
</button>
);
}
useStateからは現在の状態を持つ変数(count)と、それを更新するための関数(setCount)が得られる。
また、useStateの引数に渡しているものが初期値となる。
ボタンをクリック→クリックハンドラが呼ばれる
→クリックハンドラの中でsetCount()に値を渡す→countを更新
同じコンポーネントを複数の場所でレンダーした場合、それぞれが独自のStateを持つため、他のボタンに影響を与えない。
import { useState } from 'react';
export default function App() {
return (
<div>
<h1>ボタンをクリックしてください</h1>
<MyButton /> {/* Aボタン */}
<MyButton /> {/* Bボタン */}
</div>
);
}
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
{count}回クリックされました
</button>
);
}
コンポーネント間でのデータの共有
コンポーネント間でデータを共有したい場合は、stateをデフォルトコンポーネントに実装し、各コンポーネントにstateと共通のイベントハンドラを渡す。
import { useState } from 'react';
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>ボタンをクリックしてください</h1>
<MyButton count={count} onClick={handleClick} /> {/* Aボタン */}
<MyButton count={count} onClick={handleClick} /> {/* Bボタン */}
</div>
);
}
function MyButton() {
return (
<button onClick={handleClick}>
{count}回クリックされました
</button>
);
}
フック(Hook)の使用
useで始まる関数はフック(Hook)と呼び、useStateはユーザーの入力などをコンポーネントに記憶させるフック。
JavaScriptの関数として、独自のカスタムフックを定義することもできる。
■ useState以外のフック
フックを呼び出す場合は、コンポーネントを定義した記述の直下でのみ呼び出すことができる。
条件分岐やループの中でuseStateを使用したい場合は、新しいコンポーネントを作成し、そこで呼び出す必要がある。
import { useState } from 'react';
const products = [
{ title: 'キャベツ', id: 1, favorite_flg: true },
{ title: 'ガーリック', id: 2, favorite_flg: false },
{ title: 'りんご', id: 3, favorite_flg: false },
];
const listItems = products.map(product =>
// 条件分岐やループの中でuseStateは使用できない
const [isFavorite, setIsFavorite] = useState(false);
<li key={product.id}>
{product.title}
<button onClick={() => setIsFavorite(isFavorite ? false : true)}>
{isFavorite ? '❤️' : '🖤'}
</button>
</li>
);
export default function MyApp() {
return (
<ul>{listItems}</ul>
);
}
// ループ内の処理をコンポーネントとして書き出す
import { useState } from 'react';
const products = [
{ title: 'キャベツ', id: 1, favorite_flg: true },
{ title: 'ガーリック', id: 2, favorite_flg: false },
{ title: 'りんご', id: 3, favorite_flg: false },
];
function ProductItem({ product }) {
// コンポーネント定義の直下なのでフックが使える。
// useState() の引数に product.favorite_flg を渡すと配列の初期値(true/false)が画面に反映される
const [isFavorite, setIsFavorite] = useState(product.favorite_flg);
return (
<li>
{product.title}
<button onClick={() => setIsFavorite(isFavorite ? false : true)}>
{isFavorite ? '❤️' : '🖤'}
</button>
</li>
);
}
export default function App() {
return (
<ul>
{products.map((product) => (
// ループの中では、上で作ったコンポーネントを呼び出すだけ
<ProductItem key={product.id} product={product} />
))}
</ul>
);
}
■ カスタムフック
独自で作成した関数を持つことができる。
returnで使用したい関数名を渡して受け取り側で使用する。
export default function App() {
const {handleDeleteTodo} = useTodo()
}
function useTodo() {
const initTodos = [
{id: 1, todo: 'test'},
{id: 2, todo: 'test2'}
]
const [todos, setTodos] = useState(initTodos)
const handleDeleteTodo = (id) => {
const filterTodos = todos.filter(t => t.id !== id)
setTodos(filterTodos)
}
return {
handleDeleteTodo
}
}
まとめ
■ 用語
フック(Hook)
Reactが公式に提供している便利な関数。関数名が必ずuseから始まる。
useState(データを保持するための変数と、それを更新する関数を作る関数)やuseEffect(画面描画時に処理をする関数)などがある。
プロップス(Props)
親から子コンポーネントへデータを引き渡すためのHTMLの属性のような仕組み。
子コンポーネント側は引数として受け取る。
イベントハンドラ
イベントが発火した際に実行される関数。
■イベントハンドラについて
onClick={handleClick()}
画面が描画されるタイミングで関数が1回のみ実行される。
onClick={handleClick}
イベントが発火した際に関数が実行される。
onClick={() => handleClick(1)}
イベントが発火した際に関数が実行される(関数に引数を渡したい場合)。