TL;DR
このページでは、JSX の基礎の実装方法について解説しますね。一言でいうと JSX とは、JavaScript 内で HTML のような記述を可能にする記法です。React や Next.js を使った開発において必要不可欠な知識となります。
開発環境 | バージョン |
---|---|
Next.js | 13.4.4 |
TypeScript | 5.0.4 |
Emotion | 11.11.0 |
React | 18.2.0 |
JSX とは?
JSX (JavaScript XML) は、React や Next.js で利用される JavaScript の拡張文法です。これにより、JavaScript の中で直感的に HTML のようなマークアップが記述でき、見た目に関する部分とロジックを一元的に扱うことができます。
基本的な JSX のコードは以下のようになります。
const element = <h1>Hello, world!</h1>
ここで <h1>Hello, world!</h1>
が JSX の部分で、HTML のように見えますが実は JavaScript の一部です。JSX の特徴は、タグの中に JavaScript の変数や関数を {}
で囲んで埋め込むことができる点です。この機能により、動的な UI の生成が容易となります。
例えば、以下のコードは変数 name
を JSX 内で参照しています。
const name = 'John Doe'
const element = <h1>Hello, {name}</h1>
JSX の表現力はこれだけに留まりません。条件分岐や配列の操作など、JavaScript のあらゆる機能を駆使することが可能です。
JSX のルール
JSX には特定のルールと特性がありますので、それらについて詳しく説明いたします。
最上位の要素は一つ
JSX では、一つのコンポーネント内で複数の要素を返す場合、それら全ての要素は一つの親要素で囲まれなければなりません。
const ValidComponent = () => {
return (
<div>
<h1>Hello, world!</h1>
<p>Welcome to my website.</p>
</div>
)
}
フラグメント
ただし、全ての要素を無意味な <div>
タグで囲むのは好ましくない場合もあります。そのような場合、React はフラグメントという機能を提供しています。フラグメントは <></>
タグで表され、これにより無駄なタグを生成せずに複数の要素を返すことができます。
const FragmentComponent = () => {
return (
<>
<h1>Hello, world!</h1>
<p>Welcome to my website.</p>
</>
)
}
要素は閉じる
全ての JSX タグは閉じる必要があります。通常の HTML では閉じタグが省略できる要素がありますが(例えば、<input>
、<img>
など)、JSX ではそれが許可されません。JSX 内では、閉じタグを省略すると構文エラーが発生します。
以下のように自己閉鎖タグ(self-closing tag)を使います。
const ValidComponent = () => <input type="text" />
className
属性
HTML の class
属性は JavaScript の予約語なので、JSX では代わりに className
属性を使用します。
const ClassComponent = () => <div className="myClass">Hello, world!</div>
JavaScript Expression
JSX の中に JavaScript の式(expression)を埋め込むことができます。これは {}
カッコで囲むことで行います。
const data = {
name: 'John Doe',
age: 30,
}
const ExpressionComponent = () => {
return (
<div>
<h1>Hello, {data.name}!</h1>
<p>You are {data.age} years old.</p>
</div>
)
}
コンポーネントの生成
JSX は新しいコンポーネントを生成するのにも使用できます。新しいコンポーネントは大文字で始まる必要があり、小文字で始まるものは HTML タグとして解釈されます。
import MyComponent from './MyComponent'
const Component = () => {
return <MyComponent />
}
ここでは、MyComponent
という新しいコンポーネントをインポートして使用しています。<MyComponent />
は JSX で新たなコンポーネントを生成します。
プロップスの渡し方
JSX を使用して、親コンポーネントから子コンポーネントへデータを渡すことができます。これを"props(プロパティ)"といいます。これにより、コンポーネント間でデータを共有できます。
const ChildComponent = ({ text }: { text: string }) => {
return <p>{text}</p>
}
const ParentComponent = () => {
return <ChildComponent text="Hello from Parent Component" />
}
ここでは、ParentComponent
からChildComponent
にテキストデータをプロップスとして渡しています。
子要素の使用
JSX では、props.children
という特別なプロップを使用して、コンポーネントの開始タグと終了タグの間にあるコンテンツにアクセスできます。
type ContainerProps = {
children: React.ReactNode
}
const Container = ({ children }: ContainerProps) => {
return <div>{children}</div>
}
const Component = () => {
return (
<Container>
<p>Hello, world!</p>
</Container>
)
}
ここでは、Container
コンポーネントがchildren
プロップを受け取り、それを描画しています。Component
コンポーネントでは、Container
コンポーネントの中に<p>
タグを配置しています。
インラインスタイルの適用
JSX では、インラインスタイルを適用するためにはオブジェクトの形式でスタイルを指定します。そして、プロパティ名は camelCase(キャメルケース)で記述します。
const Component = () => {
return <div style={{ color: 'red', fontSize: '16px' }}>Hello, world!</div>
}
上記の例では、<div>
要素に対してインラインスタイルを適用しています。色を赤にし、フォントのサイズを 16px に設定しています。
JavaScript 式の埋め込み
JSX では、波括弧 {}
を使用して JavaScript 式を埋め込むことができます。これを使うと、動的なコンテンツを JSX 要素の中に直接書き込むことができます。
const Component = () => {
const name = 'World'
return <div>Hello, {name}!</div>
}
上記の例では、name
という変数を定義し、それを波括弧を使用して <div>
要素の中に埋め込んでいます。
ブール値、Null、Undefined は表示されない
JSX では、ブール値(true
やfalse
)、null
、そしてundefined
はレンダリングされません。そのため、これらを返すことでコンポーネントの出力を制御することができます。
const Component = ({ shouldDisplay }: { shouldDisplay: boolean }) => {
if (!shouldDisplay) {
return null
}
return <div>Hello, world!</div>
}
上記の例では、shouldDisplay
というプロップがfalse
のときにnull
を返しています。これにより、<div>
要素は表示されません。
関数を渡す
JSX では、要素の属性として関数を渡すことができます。これは、特にイベントハンドラ(onClick、onChange など)に関数を渡す際によく使います。
const Component = () => {
const handleClick = () => {
alert('Button clicked!')
}
return <button onClick={handleClick}>Click me</button>
}
この例では、handleClick
という関数を定義し、その関数をonClick
属性としてボタン要素に渡しています。このボタンがクリックされると、handleClick
関数が呼び出され、アラートが表示されます。
配列をレンダリングする
JSX では、要素の子として配列を使用することができます。これを使用すると、配列の各項目を要素としてレンダリングすることができます。
const Component = () => {
const items = ['Apple', 'Banana', 'Cherry']
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
)
}
この例では、items
という配列を定義し、その配列を.map
メソッドで処理しています。この処理により、配列の各項目を<li>
要素としてレンダリングしています。
JSX におけるコメントの書き方
JSX では、JavaScript の通常のコメント//
や/* */
は使用できません。その代わりに、JSX 内でコメントを書くためには{/* Comment */}
のように、波括弧とアスタリスクを使った記法を使用します。
const Component = () => (
<div>
{/* This is a comment */}
<p>Hello, world!</p>
</div>
)
複数の要素をラップするための React.Fragment
React.Fragment (または短縮形として <>
と </>
) を使うことで、複数の JSX 要素を一つの要素にまとめることができます。これは、特に React が単一の親要素を必要とする場合に便利です。
const Component = () => (
<>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</>
)
制御構文の活用
JSX では、JavaScript の制御構文(if
、for
など)を直接書くことはできませんが、波括弧{}
を使って JavaScript の式を埋め込むことができます。そのため、複雑な制御フローを扱う場合でも、適切な JavaScript の式を作成することで対応可能です。
const Component = ({ condition }) => <div>{condition ? <p>Condition is true</p> : <p>Condition is false</p>}</div>
コンポーネントへの参照(ref)
React では、DOM 要素への参照を保存するために ref を使うことがあります。これは、フォーカスの管理、テキストの選択、メディアの再生など、直接的な DOM 操作が必要となる場合に役立ちます。
JSX では、ref を使って次のようにコンポーネントへの参照を作成することができます。
import { useRef } from 'react'
const Component = () => {
const inputRef = useRef(null)
const handleClick = () => {
// refを通じてDOMにアクセス
inputRef.current.focus()
}
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>Focus the input</button>
</div>
)
}
このコードでは、useRef
フックを使って新たな ref を作成し、その ref を <input>
要素に設定しています。そして、ボタンがクリックされたとき、その ref を使って直接 <input>
要素にアクセスし、その要素にフォーカスを当てるようにしています。
Emotion
Emotion は CSS-in-JS ライブラリの一つで、コンポーネントのスタイリングを行うために利用されます。
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
const Component = () => (
<div
css={css`
color: hotpink;
`}
>
This is hotpink.
</div>
)
@emotion/react
の css
関数を使って CSS を記述し、JSX 要素の css
プロパティに渡します。
Styled Components
Styled Components も CSS-in-JS ライブラリの一つで、特定のコンポーネントにスタイルを適用するために用いられます。
import styled from 'styled-components'
const StyledDiv = styled.div`
color: hotpink;
`
const Component = () => <StyledDiv>This is hotpink.</StyledDiv>
styled
関数を使って新しいスタイリングされたコンポーネントを作成します。そのコンポーネントは JSX でそのまま使用することができます。
いずれのライブラリも、JSX の中で直接スタイルを記述して適用することができ、コンポーネントの見た目と機能を密接に連携させることができます。このため、コンポーネントの再利用性と保守性が向上します。
React Router
React Router は React アプリケーションにルーティング機能を提供するライブラリです。<Route>
や <Link>
などの JSX 要素を提供しています。
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
const App = () => (
<Router>
<div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
</Router>
)
Material-UI
Material-UI は React 用の UI コンポーネントライブラリで、さまざまな UI コンポーネントを JSX 要素として提供しています。
import Button from '@material-ui/core/Button'
const App = () => (
<Button variant="contained" color="primary">
Hello World
</Button>
)
Ant Design
Ant Design はエンタープライズレベルの UI デザイン体系と React ベースの実装を提供するライブラリで、たくさんの独自の JSX コンポーネントを提供しています。
import { Button } from 'antd'
const App = () => <Button type="primary">Click Me</Button>
React Bootstrap
React Bootstrap は、Bootstrap の React 版で、Bootstrap の各コンポーネントを React コンポーネントとして提供します。
import Button from 'react-bootstrap/Button'
const App = () => <Button variant="primary">Click Me</Button>
Next.js で、JSX を実装
では、Next.js と TypeScript を使って、具体的に JSX の基礎を学んでいきましょう。
components/Hello.tsx
type Props = {
name: string
}
const Hello = ({ name }: Props) => {
return <h1>Hello, {name}</h1>
}
export default Hello
ここでは Hello
というコンポーネントを定義しています。このコンポーネントは name
というプロパティを受け取り、<h1>
タグの中に "Hello, {name}" と表示します。{name}
の部分は JSX の中で JavaScript の変数を参照しています。
pages/index.tsx
import Hello from '../components/Hello'
const Home = () => {
return <Hello name="World" />
}
export default Home
ここでは先程作成した Hello
コンポーネントを import
し、name
プロパティに "World" を渡して呼び出しています。その結果、ブラウザ上で "Hello, World" と表示されます。
JSX を実装
では、さらに具体的な JSX のコードを見てみましょう。
type PersonProps = {
name: string
age: number
}
const Person = ({ name, age }: PersonProps) => {
return (
<div>
<h1>{name}</h1>
<p>{age} years old</p>
</div>
)
}
ここで定義した Person
コンポーネントは、name
と age
という 2 つのプロパティを受け取り、それらを元に HTML に似たマークアップを生成します。<div>
タグで囲んだ複数の JSX 要素は、一つの親要素に囲まれているため、正しい JSX の形となっています。
また、このコンポーネントを使う側は以下のように書くことができます。
const App = () => {
return (
<div>
<Person name="John Doe" age={22} />
<Person name="Jane Doe" age={24} />
</div>
)
}
ここで、Person
コンポーネントを 2 回呼び出し、それぞれ異なるプロパティを渡しています。これにより、同じ Person
コンポーネントを利用しつつも、それぞれ異なる内容を表現することができます。