前回
概要
新しい、公式サイトは日本語訳がされてなくて不便なので、deeplを使って訳して読んでいくだけ。
感想とかのノイズは極力入れないつもりなので、さらっと見てもらえると
JavaScript in JSX with Curly Braces
JSXは、JavaScriptファイルの中にHTMLのようなマークアップを書くことができ、レンダリングロジックとコンテンツを同じ場所に保持することができます。マークアップの中にJavaScriptのロジックを追加したり、動的なプロパティを参照したりしたい場合もあります。このような場合、JSXの中で中括弧を使用することで、JavaScriptのウィンドウを開くことができます。
以下のことを学びます。
- 引用符を使った文字列の受け渡し方法
- JSXの内部で中括弧を使ってJavaScriptの変数を参照する方法
- JSX内で中括弧を使用してJavaScript関数を呼び出す方法
- JSX内で中括弧を使用してJavaScriptオブジェクトを使用する方法
Passing strings with quotes
JSXに文字列属性を渡したいときは、一重引用符や二重引用符で囲んで渡します:
export default function Avatar() {
return (
<img
className="avatar"
src="https://i.imgur.com/7vQD0fPs.jpg"
alt="Gregorio Y. Zara"
/>
);
}
ここでは、"https://i.imgur.com/7vQD0fPs.jpg "と "Gregorio Y. Zara "が文字列として渡されています。
しかし、srcやaltテキストを動的に指定したい場合はどうすればよいでしょうか。JavaScriptの「と」を「{と}」に置き換えて、その値を利用することができます:
export default function Avatar() {
const avatar = 'https://i.imgur.com/7vQD0fPs.jpg';
const description = 'Gregorio Y. Zara';
return (
<img
className="avatar"
src={avatar}
alt={description}
/>
);
}
画像を丸くするCSSクラス名「avatar」を指定するclassName="avatar "と、avatarというJavaScript変数の値を読み取るsrc={avatar}の違いに注目してください。これは、中括弧を使うことで、マークアップの中でJavaScriptを扱うことができるからです!
Using curly braces: A window into the JavaScript world
JSXは、JavaScriptの特殊な記述方法です。つまり、中括弧{ }で囲むことで、その中でJavaScriptを使用することができるのです。以下の例では、まず科学者の名前であるnameを宣言し、それを中括弧で囲んで<h1>
の中に入れています:
export default function TodoList() {
const name = 'Gregorio Y. Zara';
return (
<h1>{name}'s To Do List</h1>
);
}
名前の値を「Gregorio Y. Zara」から「Hedy Lamarr」に変えてみてください。リストのタイトルがどう変わるかわかりますか?
formatDate()のような関数呼び出しも含めて、中括弧の間ではどんなJavaScriptの式でも使えます:
const today = new Date();
function formatDate(date) {
return new Intl.DateTimeFormat(
'en-US',
{ weekday: 'long' }
).format(date);
}
export default function TodoList() {
return (
<h1>To Do List for {formatDate(today)}</h1>
);
}
whrere to use curly braces
JSX内部では中括弧は2通りしか使えません:
- JSXのタグの中に直接テキストとして入れる:
<h1>
{名前}のTo Doリスト</h1>
は機能しますが、<{tag}>Gregorio Y. ZaraのTo Doリスト{tag}>は機能しません。 - また、=記号の直後の属性として、src={avatar}はavatar変数を読み込みますが、src="{avatar}"は"{avatar}"という文字列を渡します
Using “double curlies”: CSS and other objects in JSX
JSXでは、文字列や数値などのJavaScript表現に加えて、オブジェクトを渡すこともできます。オブジェクトも中括弧で表し、{ name: "Hedy Lamarr", inventions:5 }.したがって、JSXでJSオブジェクトを渡すには、そのオブジェクトを別の中括弧で囲む必要があります: person={{ name: "Hedy Lamarr", inventions:5 }}.
JSXのインラインCSSスタイルで見ることがあります。Reactでは、インラインスタイルを使用する必要はありません(ほとんどの場合、CSSクラスが有効に機能します)。しかし、インラインスタイルが必要な場合は、style属性にオブジェクトを渡します:
export default function TodoList() {
return (
<ul style={{
backgroundColor: 'black',
color: 'pink'
}}>
<li>Improve the videophone</li>
<li>Prepare aeronautics lectures</li>
<li>Work on the alcohol-fuelled engine</li>
</ul>
);
}
backgroundColorとcolorの値を変えてみてください。
このように書くと、中括弧の中のJavaScriptオブジェクトが本当によくわかります:
<ul style={
{
backgroundColor: 'black',
color: 'pink'
}
}>
次にJSXで{{と}}を見かけたら、それはJSXのcurliesの中にあるオブジェクトに過ぎないということを知っておいてください!
インラインのスタイル・プロパティは、キャメルケースで記述されます。例えば、HTMLの
- は、コンポーネントでは
const person = {
name: 'Gregorio Y. Zara',
theme: {
backgroundColor: 'black',
color: 'pink'
}
};
export default function TodoList() {
return (
<div style={person.theme}>
<h1>{person.name}'s Todos</h1>
<img
className="avatar"
src="https://i.imgur.com/7vQD0fPs.jpg"
alt="Gregorio Y. Zara"
/>
<ul>
<li>Improve the videophone</li>
<li>Prepare aeronautics lectures</li>
<li>Work on the alcohol-fuelled engine</li>
</ul>
</div>
);
}
この例では、person JavaScriptオブジェクトは、名前文字列とテーマオブジェクトを含んでいます:
const person = {
name: 'Gregorio Y. Zara',
theme: {
backgroundColor: 'black',
color: 'pink'
}
};
コンポーネントは、次のようにpersonからこれらの値を使用することができます:
<div style={person.theme}>
<h1>{person.name}'s Todos</h1>
これで、JSXのほぼ全てが分かったと思います:
引用符で囲まれたJSXの属性は、文字列として渡されます。
中括弧は、JavaScriptのロジックや変数をマークアップに取り込むことができます。
JSXタグのコンテンツの中や、属性の「=」の直後で機能します。
{や}}は特別な構文ではありません。JSX中括弧の中に収められたJavaScriptオブジェクトです。
Try out some challenges
チャレンジ1/3:ミスを修正する
このコードは、Objects are not valid as a React childというエラーでクラッシュします:
私の回答
const person = {
name: 'Gregorio Y. Zara',
theme: {
backgroundColor: 'black',
color: 'pink'
}
};
export default function TodoList() {
return (
<div style={person.theme}>
<h1>{person.name}'s Todos</h1>
<img
className="avatar"
src="https://i.imgur.com/7vQD0fPs.jpg"
alt="Gregorio Y. Zara"
/>
<ul>
<li>Improve the videophone</li>
<li>Prepare aeronautics lectures</li>
<li>Work on the alcohol-fuelled engine</li>
</ul>
</div>
);
}
チャレンジ2/3:情報をオブジェクトに抽出する
画像のURLを人物オブジェクトに抽出する。
私の回答
const person = {
name: 'Gregorio Y. Zara',
theme: {
backgroundColor: 'black',
color: 'pink'
},
url: "https://i.imgur.com/7vQD0fPs.jpg"
};
export default function TodoList() {
return (
<div style={person.theme}>
<h1>{person.name}'s Todos</h1>
<img
className="avatar"
src={person.url}
alt="Gregorio Y. Zara"
/>
<ul>
<li>Improve the videophone</li>
<li>Prepare aeronautics lectures</li>
<li>Work on the alcohol-fuelled engine</li>
</ul>
</div>
);
}
チャレンジ3/3:JSXの中括弧の中に式を書く
以下のオブジェクトでは、完全な画像URLが、ベースURL、imageId、imageSize、およびファイル拡張子の4つの部分に分割されています。
ベースURL(常に「https://i.imgur.com/」)、imageId(「7vQD0fP」)、imageSize(「s」)、ファイル拡張子(常に「.jpg」)の各属性を組み合わせた画像URLが欲しい。しかし、タグがsrcを指定する方法がおかしいのです。
直せますか?
私の回答
const baseUrl = 'https://i.imgur.com/';
const person = {
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP',
imageSize: 's',
theme: {
backgroundColor: 'black',
color: 'pink'
}
};
export default function TodoList() {
return (
<div style={person.theme}>
<h1>{person.name}'s Todos</h1>
<img
className="avatar"
src={`${baseUrl}${person.imageId}${person.imageSize}.jpg`}
alt={person.name}
/>
<ul>
<li>Improve the videophone</li>
<li>Prepare aeronautics lectures</li>
<li>Work on the alcohol-fuelled engine</li>
</ul>
</div>
Passing Props to a Component
Reactのコンポーネントは、互いに通信するためにpropsを使用します。すべての親コンポーネントは、子コンポーネントにpropsを与えることで、何らかの情報を渡すことができます。propsはHTMLの属性を連想させるかもしれませんが、オブジェクト、配列、関数など、あらゆるJavaScriptの値をpropsに渡すことができます。
Familiar props
プロップとは、JSXタグに渡す情報のことです。例えば、className、src、alt、width、heightは、に渡すことができるプロップの一部です:
function Avatar() {
return (
<img
className="avatar"
src="https://i.imgur.com/1bX5QH6.jpg"
alt="Lin Lanying"
width={100}
height={100}
/>
);
}
export default function Profile() {
return (
<Avatar />
);
}
<img>
タグに渡せるpropsはあらかじめ定義されています(ReactDOMはHTML標準に準拠しています)。しかし、<Avatar>
のような独自のコンポーネントには、任意のpropsを渡してカスタマイズすることができます。その方法を説明します!
このコードでは、Profileコンポーネントが子コンポーネントのAvatarにpropsを渡していません:
export default function Profile() {
return (
<Avatar />
);
}
アバターには、2つのステップで小道具を与えることができます。
ステップ1:子コンポーネントにpropsを渡す
まず、Avatarにいくつかのpropsを渡します。例えば、person(オブジェクト)とsize(数値)の2つのプロップを渡してみましょう:
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
もし、person=の後の二重の中括弧があなたを混乱させるなら、それは単にJSXの中括弧の中のオブジェクトに過ぎないことを思い出してください。
これで、これらのプロップをアバターコンポーネントの内部で読むことができるようになりました。
ステップ2:子コンポーネント内のpropsを読み込む
これらのプロップは、関数Avatarの直後に、人名とサイズをカンマで区切って({と})記載することで読み取ることができます。これにより、Avatarのコード内で、変数と同じようにこれらを使用することができます。
function Avatar({ person, size }) {
// person and size are available here
}
レンダリングに人物とサイズのプロップを使用するロジックをAvatarに追加すれば完了です。
これで、さまざまなプロップを使ってさまざまな方法でレンダリングするようにAvatarを設定することができます。値を微調整してみる
App.js
import { getImageUrl } from './utils.js';
function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
export default function Profile() {
return (
<div>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
<Avatar
size={280}
person={{
name: 'Aklilu Lemma',
imageId: 'OKS67lh'
}}
/>
<Avatar
size={150}
person={{
name: 'Lin Lanying',
imageId: '1bX5QH6'
}}
/>
</div>
);
}
util.js
export function getImageUrl(person, size = 's') {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
プロップを使えば、親と子のコンポーネントを独立して考えることができます。たとえば、Profileの中にあるpersonやsizeのプロップを変更しても、Avatarがそれらをどのように使用するかは考える必要はありません。同様に、Profileを見ることなく、Avatarがこれらのプロップをどのように使うかを変更することができます。
小道具は、あなたが調整できる「つまみ」のようなものだと考えることができます。プロップは関数の引数と同じような役割を果たします!Reactコンポーネントの関数は、1つの引数、propsオブジェクトを受け取ります
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
通常、プロップスオブジェクト全体は必要ないので、個々のプロップスにデストラクションします。
小道具を宣言するときは、( と ) の中に { と } が一組入っているのを見逃さないようにしましょう:
function Avatar({ person, size }) {
// ...
}
この構文は「デストラクチャリング」と呼ばれ、関数のパラメータからプロパティを読み取るのと同じです:
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
Specifying a default value for a prop
もし、値が指定されなかったときに、その値に戻るようなデフォルト値を与えたい場合は、パラメータの直後に=とデフォルト値を置くことで、デストラクチャリングでそれを行うことができます:
function Avatar({ person, size = 100 }) {
// ...
}
これで、<Avatar person={...} />
がサイズプロップなしでレンダリングされた場合、そのサイズは100になります。/> がサイズプロップなしでレンダリングされた場合、サイズは100に設定されます。
デフォルト値は、size propがない場合、またはsize={undefined}を渡した場合のみ使用されます。しかし、size={null}やsize={0}を渡すと、デフォルト値は使われません。
Forwarding props with the JSX spread syntax
時には小道具を渡すことがとても繰り返しになります:
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
繰り返しの多いコードに問題はありませんし、より読みやすくなることもあります。しかし、時には簡潔さを重視することもあるでしょう。コンポーネントによっては、このプロファイルがアバターで行っているように、すべてのプロップを子に転送するものがあります。これらのコンポーネントはプロップを直接使用しないので、より簡潔な「spread」構文を使用することが理にかなっている場合があります:
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
これは、プロフィールの小道具をすべてアバターに転送するもので、それぞれの名前を列挙する必要はありません。
spread構文は自制して使ってください。もし他のすべてのコンポーネントで使っているのなら、何かが間違っています。多くの場合、コンポーネントを分割して、子コンポーネントをJSXとして渡す必要があることを示しています。これについては、次に詳しく説明します!
Passing JSX as children
ブラウザの内蔵タグをネストするのが一般的です:
<div>
<img />
</div>
同じように自作のコンポーネントをネストさせたい場合もあるでしょう:
<Card>
<Avatar />
</Card>
JSXタグの中にコンテンツをネストすると、親コンポーネントはそのコンテンツをchildrenと呼ばれるpropで受け取ります。例えば、以下のCardコンポーネントは、<Avatar />
に設定されたchildrenプロパティを受け取り、ラッパーのdivにレンダリングします:
App.js
import Avatar from './Avatar.js';
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
</Card>
);
}
... Avater.js
については割愛
<Card>
内の <Avatar>
をテキストに置き換えてみて、Card コンポーネントがどのようにネストしたコンテンツをラップできるかを確認してみてください。カードコンポーネントは、その内部で何がレンダリングされているかを「知る」必要はないのです。このような柔軟なパターンは、いろいろなところで目にすることができます。
childrenプロップを持つコンポーネントは、任意のJSXで親コンポーネントが「埋める」ことができる「穴」を持っていると考えることができます。パネルやグリッドなどのビジュアルラッパーにchildrenプロップを使うことが多いでしょう。
How props change over time
下のClockコンポーネントは、親コンポーネントからcolorとtimeの2つのpropsを受け取っています。(親コンポーネントのコードは、状態を使用しているため省略されていますが、ここではまだ説明しません)
下のセレクトボックスの色を変えてみてください:
export default function Clock({ color, time }) {
return (
<h1 style={{ color: color }}>
{time}
</h1>
);
}
この例では、コンポーネントが時間とともに異なるプロップを受け取る可能性があることを説明しています。プロップは常に固定されているわけではありません!ここでは、時間プロップは1秒ごとに変化し、色プロップは他の色を選択すると変化します。プロップは、コンポーネントのデータを、最初だけでなく、どの時点でも反映します。
この例では、コンポーネントが時間とともに異なるプロップを受け取る可能性があることを説明しています。プロップは常に固定されているわけではありません!ここでは、時間プロップは1秒ごとに変化し、色プロップは他の色を選択すると変化します。プロップは、コンポーネントのデータを、最初だけでなく、どの時点でも反映します。
小道具を変える」ことはしないようにしましょう。ユーザーの入力に反応する必要がある場合(選択した色を変更するような場合)、「状態を設定する」必要がありますが、これについては「状態」で学ぶことができます:これは「ステート:コンポーネントの記憶」で学ぶことができます。
まとめ
- propを渡すには、HTMLの属性と同じように、JSXにpropを追加します。
- プロップを読み込むには、function Avatar({ person, size })デストラクチャリング構文を使用します。
- size = 100 のようにデフォルト値を指定することができ、これは欠損や未定義の小道具に使用されます。
-
<Avatar {...props} />
ですべてのpropsを転送することができます。JSXスプレッド構文で転送できますが、使い過ぎないようにしましょう! -
<Card><Avatar /></Card>
のようなネストしたJSXは、Cardコンポーネントの子プロップとして表示されます。 - レンダリングごとに新しいバージョンのpropsを受信します。
- プロップを変更することはできません。インタラクティブ性が必要な場合は、ステートを設定する必要があります。
Try out some challenges
チャレンジ1/3:コンポーネントを抽出する
このGalleryコンポーネントには、2つのプロファイルのための非常に類似したマークアップが含まれています。重複を減らすために、このコンポーネントからProfileコンポーネントを抽出します。このコンポーネントに渡す小道具を選択する必要があります。
私の回答
App.js
import { getImageUrl } from './utils.js';
const Profile = ({name, imageId, profession, award, discovered }) => (
<section className="profile">
<h2>{name}</h2>
<img
className="avatar"
src={getImageUrl(imageId)}
alt={name}
width={70}
height={70}
/>
<ul>
<li>
<b>Profession: </b>
{profession}
</li>
<li>
<b>Awards: {award.count} </b>
{award.name}
</li>
<li>
<b>Discovered: </b>
{discovered}
</li>
</ul>
</ section>
)
const novelists = [
{
name: 'Maria Skłodowska-Curie',
imageId: 'szV5sdG',
profession: 'physicist and chemist',
award: {
count: 4,
name: '(Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal)'
},
discovered: 'polonium (element)'
},
{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2',
profession: 'geochemist',
award: {
count: 2,
name: '(Miyake Prize for geochemistry, Tanaka Prize)'
},
discovered: 'a method for measuring carbon dioxide in seawater'
},
]
export default function Gallery() {
return (
<div>
<h1>Notable Scientists</h1>
{novelists.map(novelist => (
<Profile
key={novelist.imageId}
{...novelist}
/>)
)}
</div>
);
}
チャレンジ2/3:プロップに基づいて画像サイズを調整する
この例では、Avatarは<img>
の幅と高さを決定する数値のsize propを受け取ります。この例では、サイズプロップは40に設定されています。しかし、この画像を新しいタブで開くと、画像自体が大きくなっている(160ピクセル)ことに気づくでしょう。実際の画像サイズは、どのサムネイルサイズを要求しているかで決まります。
サイズプロップに基づいて、最も近い画像サイズを要求するようにアバターコンポーネントを変更します。具体的には、サイズが90未満の場合、getImageUrl関数に「b」(「big」)ではなく、「s」(「small」)を渡します。サイズプロップの値が異なるアバターをレンダリングし、新しいタブで画像を開いて、変更がうまくいくことを確認してください。
私の回答
App.js
import { getImageUrl } from './utils.js';
function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person, size < 90 ? 's' : 'b')}
alt={person.name}
width={size}
height={size}
/>
);
}
export default function Profile() {
return (
<Avatar
size={40}
person={{
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP'
}}
/>
);
}
回答で気になったもの
https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
size * ratio > 90
また、window.devicePixelRatioを考慮することで、高DPI画面に対してよりシャープな画像を表示することができます:
チャレンジ3/3:children propでJSXを渡す
以下のマークアップからCardコンポーネントを抽出し、children propを使用して異なるJSXを渡します
私の回答
App.js
const Card = ({children}) => (
<div className="card">
<div className="card-content">
{children}
</div>
</div>
)
export default function Profile() {
return (
<div>
<Card>
<h1>Photo</h1>
<img
className="avatar"
src="https://i.imgur.com/OKS67lhm.jpg"
alt="Aklilu Lemma"
width={70}
height={70}
/>
</Card>
<Card>
<h1>About</h1>
<p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
</Card>
</div>
);
}
次回