TL;DR
このページでは、Next.js と TypeScript の実装方法について深掘りし、React の Hooks の一つである useState の使い方を解説します。useState の基本的な概念から高度なテクニックまで、初心者から上級者まで参考になる内容を提供します。
開発環境 | バージョン |
---|---|
Next.js | 13.4.3 |
TypeScript | 5.0.4 |
Emotion | 11.11.0 |
React | 18.2.0 |
useState は、状態管理
useState は React の Hooks の一つで、関数コンポーネントの中で状態を管理するためのものです。もし、状態を持たせたいコンポーネントがあれば、useState を使うと便利です。
では、まず「状態」とは何か、そしてそれがなぜ必要なのか、それを知るための一つの例を考えてみましょう。
皆さんは、スーパーマーケットで買い物をした経験があると思います。スーパーマーケットにはたくさんの商品が並んでいますよね。それぞれの商品は、あなたがそれをカートに入れるまでは「買わない状態」にあります。
でも、あなたが「おいしいそうなパン」を見つけてカートに入れたとき、そのパンは「買う状態」に変わります。また、カートに入れたパンを「やっぱりこれはいらないな」と思ってカートから出すと、そのパンは再び「買わない状態」に戻ります。
このように、あなたがスーパーマーケットで買い物をするときの「商品の買う/買わない」の状態のように、アプリケーションでも多くのものが「ある状態」から「別の状態」に変化します。そして、それらの状態を管理するのが「useState」なんです。
例えば、ユーザーがフォームに何かを入力したとき、その入力値は状態として保存され、その後ユーザーが別の値を入力すると、その新しい値で状態が更新されます。このように useState を使うと、アプリケーションの状態を簡単に管理できるんです。
ですから、React でアプリケーションを作るとき、useState はとても大切なツールなんですよ。
基本的な使用方法は以下の通りです。
import { useState } from 'react'
type Props = {
initialCount: number
}
const Counter = ({ initialCount }: Props) => {
const [count, setCount] = useState(initialCount)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
)
}
useState は一つの引数を取り、その引数は状態の初期値です。この例では、初期値は initialCount
として渡されます。
また、useState は 2 つの要素を持つ配列を返します。一つ目の要素は現在の状態、二つ目の要素はその状態を更新する関数(セッター)です。この例では、現在のカウント値が count
という名前で、それを更新する関数が setCount
という名前で取り扱われています。
Next.js での useState の使用例
では、Next.js プロジェクトで useState をどのように使うのか具体的に見てみましょう。この例では、ユーザーの入力を状態として保持し、それを表示するシンプルなフォームを作成します。
ファイル名:components/InputForm.tsx
import { useState } from 'react'
type Props = {
placeholder: string
}
const InputForm = ({ placeholder }: Props) => {
const [input, setInput] = useState('')
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInput(event.target.value)
}
return (
<div>
<input type="text" value={input} onChange={handleInputChange} placeholder={placeholder} />
<p>入力値: {input}</p>
</div>
)
}
export default InputForm
このコンポーネントでは、入力要素の値が状態として保持され、ユーザーが入力するたびにその状態が更新されます。具体的には、ユーザーの入力に応じて input
の値が更新され、その更新された値が画面上に表示されます。このように useState を使うことで、ユーザーのアクションに応じて動的に変化する UI を簡単に実装することができます。
useState を使って配列を更新する
useState を使って配列の要素を追加する方法について解説しますね。まずは、「配列」というものを理解しましょう。
「配列」とは、まるで小さな引き出しのようなもので、その中にいろいろなものを保存することができます。それぞれの引き出しには番号がついていて、その番号を使って、どの引き出しに何が入っているかを知ったり、新しいものを入れたり、入っているものを取り出したりします。
では、React の useState を使って、配列に新しいものを追加する方法を見てみましょう。まずは新しい空の配列(空の引き出しセット)を作ります。
const [items, setItems] = useState([])
次に、新しいもの(例えば、文字列の 'apple')を配列に追加します。
setItems([...items, 'apple'])
このコードは、「今までの items(引き出しの中身全て)に'apple'を追加した新しい配列を作って、それを items に保存する」という意味です。配列の最後に新しい要素が追加されます。
次に、配列から特定の要素を削除する方法を見てみましょう。ここでは、2 番目の要素(番号は 0 から始まるので、実際には'apple')を削除します。
setItems(items.filter((item, index) => index !== 1))
このコードは、「items の中で、番号が 1 でないものだけを取り出して新しい配列を作り、それを items に保存する」という意味です。つまり、番号 1 の要素が削除されます。
最後に、配列の要素を変更する方法を見てみましょう。ここでは、1 番目の要素を'orange'に変更します。
setItems(items.map((item, index) => (index === 0 ? 'orange' : item)))
このコードは、「items の中の各要素について、その番号が 0 なら'orange'に変更し、それ以外ならそのままにして新しい配列を作り、それを items に保存する」という意味です。
以上が、useState を使って配列の要素を追加、削除、変更する方法です。これらの知識を使って、アプリケーションを作るときに色々なことができるようになりますよ。
複数の useState を使用
複数の独立した状態を持つコンポーネントを作成する場合、複数の useState を使用することがあります。例えば、名前と年齢を入力するフォームを作成する場合、以下のようになります。
ファイル名:components/MultipleInputForm.tsx
import { useState } from 'react'
const MultipleInputForm = () => {
const [name, setName] = useState('')
const [age, setAge] = useState('')
const handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value)
}
const handleAgeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setAge(event.target.value)
}
return (
<div>
<input type="text" value={name} onChange={handleNameChange} placeholder="名前" />
<input type="text" value={age} onChange={handleAgeChange} placeholder="年齢" />
<p>名前: {name}</p>
<p>年齢: {age}</p>
</div>
)
}
export default MultipleInputForm
このように、各入力フィールドごとに独立した状態(name
と age
)とその更新関数(setName
と setAge
)が定義されています。
Emotion でのスタイリング
Emotion を使用して、React コンポーネントにスタイルを適用することも可能です。次の例では、入力フォームのスタイルを変更してみましょう。
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
import { useState } from 'react'
const inputStyle = css`
margin: 10px 0;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
`
const InputFormStyled = ({ placeholder }: { placeholder: string }) => {
const [input, setInput] = useState('')
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInput(event.target.value)
}
return (
<div>
<input type="text" value={input} onChange={handleInputChange} placeholder={placeholder} css={inputStyle} />
<p>入力値: {input}</p>
</div>
)
}
export default InputFormStyled
上記の例では、inputStyle
を定義して、それを入力要素に適用しています。こうすることで、特定のスタイルを複数の要素に適用したり、スタイルを動的に変更することが可能になります。
オブジェクトを受け取る
useState
はオブジェクトも受け取ることが可能です。useState
は初期値を受け取る関数で、その初期値はどんな形でも構いません。つまり、文字列、数値、配列、オブジェクトなど、JavaScript で作成できるあらゆる形のデータをuseState
の初期値として使うことができます。
例えば、次のようにuseState
を使ってオブジェクトを管理することができます。
type PersonProps = {
name: string
age: number
}
const Component = () => {
const [person, setPerson] = useState<PersonProps>({ name: 'John', age: 20 })
const updateName = () => {
setPerson((prevPerson) => ({ ...prevPerson, name: 'Jane' }))
}
const updateAge = () => {
setPerson((prevPerson) => ({ ...prevPerson, age: 30 }))
}
return (
<div>
<div>{person.name}</div>
<div>{person.age}</div>
<button onClick={updateName}>Update Name</button>
<button onClick={updateAge}>Update Age</button>
</div>
)
}
この例では、useState
はPersonProps
型のオブジェクトを初期値としています。そして、setPerson
関数を使ってオブジェクト内のname
やage
を更新しています。
オブジェクトを更新する際は、新しいオブジェクトを作成するようにします。これは React の仮想 DOM が変更を検出しやすくするためです。そのため、オブジェクト内の特定の値を更新する際には、スプレッド構文...
を使ってオブジェクトをコピーし、その後で特定の値を更新します。
以上が、Next.js と TypeScript を使用して useState を実装する方法になります。これらの知識を活用して、様々な状況に応じた動的な UI を実装していきましょう。また、Emotion を使用すれば、スタイリングも非常に簡単に行えるので、ぜひ試してみてくださいね。