Preact チートシート【保存版】:JSX/Props/Events/Ref の書き方早見表

Preact は React の軽量な代替フレームワークとして、多くの開発者に選ばれています。React とほぼ同じ API を持ちながら、わずか 3KB というサイズで高速に動作するのが特徴です。しかし、実際にコードを書く際に「あの書き方、どうだったかな?」と迷うことはありませんか?
本記事では、Preact の基本的な書き方を網羅したチートシートとして、JSX、Props、Events、Ref の各機能について、すぐに使えるコード例とともに解説します。この記事を保存しておけば、開発中に迷ったときにすぐに参照できるでしょう。
早見表
開発中にすぐ参照できるよう、主要な書き方を早見表にまとめました。
JSX 早見表
# | 項目 | 書き方 | 例 |
---|---|---|---|
1 | 基本要素 | <div>...</div> | <div>Hello</div> |
2 | className | className="..." | <div className="container"> |
3 | インラインスタイル | style={{...}} | <div style={{ color: 'red' }}> |
4 | 三項演算子 | {条件 ? A : B} | {isActive ? <p>ON</p> : <p>OFF</p>} |
5 | 論理 AND | {条件 && 要素} | {show && <p>表示</p>} |
6 | リスト | {配列.map(...)} | {items.map(item => <li key={item.id}>{item.name}</li>)} |
7 | Fragment | <Fragment>...</Fragment> | <Fragment><h1>Title</h1><p>Text</p></Fragment> |
8 | Fragment 短縮 | <>...</> | <><h1>Title</h1><p>Text</p></> |
Props 早見表
# | 項目 | 書き方 | 例 |
---|---|---|---|
1 | 基本的な Props | (props: Type) | const Child = (props: ChildProps) => {...} |
2 | 分割代入 | ({ prop1, prop2 }: Type) | const Button = ({ label, disabled }: ButtonProps) => {...} |
3 | デフォルト値 | ({ prop = 値 }: Type) | const Counter = ({ count = 0 }: Props) => {...} |
4 | オプショナル | prop?: type | interface Props { label?: string; } |
5 | Children | children: ComponentChildren | const Card = ({ children }: { children: ComponentChildren }) => {...} |
6 | 関数 Props | onEvent: (arg: Type) => void | interface Props { onClick: (id: number) => void; } |
7 | スプレッド展開 | {...props} | <input {...inputProps} /> |
8 | Union 型 | prop: 'a' | 'b' | 'c' | status: 'success' | 'error' | 'warning' |
Events 早見表
# | イベント | 書き方 | 例 |
---|---|---|---|
1 | クリック | onClick={handler} | <button onClick={handleClick}>Click</button> |
2 | フォーム送信 | onSubmit={handler} | <form onSubmit={handleSubmit}> |
3 | 入力変更 | onInput={handler} | <input onInput={(e) => setValue(e.target.value)} /> |
4 | Change | onChange={handler} | <select onChange={handleChange}> |
5 | キーボード | onKeyDown={handler} | <input onKeyDown={(e) => { if (e.key === 'Enter') {...} }} /> |
6 | マウス Enter | onMouseEnter={handler} | <div onMouseEnter={handleEnter}> |
7 | マウス Leave | onMouseLeave={handler} | <div onMouseLeave={handleLeave}> |
8 | マウス Move | onMouseMove={handler} | <div onMouseMove={(e) => console.log(e.clientX)}> |
9 | preventDefault | e.preventDefault() | const handler = (e) => { e.preventDefault(); ... } |
10 | stopPropagation | e.stopPropagation() | const handler = (e) => { e.stopPropagation(); ... } |
Ref 早見表
# | 項目 | 書き方 | 例 |
---|---|---|---|
1 | Ref 作成 | useRef<Type>(初期値) | const inputRef = useRef<HTMLInputElement>(null); |
2 | Ref 設定 | ref={refオブジェクト} | <input ref={inputRef} /> |
3 | DOM アクセス | ref.current | inputRef.current?.focus() |
4 | コールバック Ref | ref={(el) => {...}} | <div ref={(el) => console.log(el)}> |
5 | forwardRef | forwardRef<Type, Props>((props, ref) => {...}) | const Input = forwardRef<HTMLInputElement, Props>((props, ref) => <input ref={ref} />) |
6 | useImperativeHandle | useImperativeHandle(ref, () => ({...})) | useImperativeHandle(ref, () => ({ focus: () => {...} })) |
7 | 複数 Ref(配列) | useRef<Type[]>([]) | const refs = useRef<(HTMLElement | null)[]>([]); |
8 | null チェック | if (ref.current) {...} | if (inputRef.current) { inputRef.current.focus(); } |
インポート早見表
# | 項目 | インポート文 |
---|---|---|
1 | h 関数 | import { h } from 'preact'; |
2 | Fragment | import { Fragment } from 'preact'; |
3 | ComponentChildren | import { ComponentChildren } from 'preact'; |
4 | useState | import { useState } from 'preact/hooks'; |
5 | useRef | import { useRef } from 'preact/hooks'; |
6 | useEffect | import { useEffect } from 'preact/hooks'; |
7 | forwardRef | import { forwardRef } from 'preact/compat'; |
8 | useImperativeHandle | import { useImperativeHandle } from 'preact/compat'; |
背景
Preact の特徴と位置づけ
Preact は React の API をベースに設計された軽量フレームワークです。React と同じような書き方ができるため、React の知識があればすぐに始められます。
以下の図は、Preact と React の関係性を示しています。
mermaidflowchart LR
react["React<br/>(120KB+)"]
preact["Preact<br/>(3KB)"]
preact_compat["preact/compat<br/>互換レイヤー"]
react -.互換性.-> preact_compat
preact_compat --> preact
style preact fill:#673ab8,color:#fff
style react fill:#61dafb,color:#000
Preact の主な利点は次のとおりです。
# | 特徴 | 詳細 |
---|---|---|
1 | 軽量性 | わずか 3KB のサイズで、高速な読み込みを実現 |
2 | React 互換 | React とほぼ同じ API で、学習コストが低い |
3 | パフォーマンス | 最適化された仮想 DOM で高速レンダリング |
4 | シンプル | 余計な機能を削ぎ落とし、本質的な機能に集中 |
Preact の基本構成要素
Preact でアプリケーションを構築する際、主に以下の 4 つの要素を理解する必要があります。
mermaidflowchart TB
component["コンポーネント"]
jsx["JSX<br/>UI の記述"]
props["Props<br/>データの受け渡し"]
events["Events<br/>ユーザー操作"]
ref["Ref<br/>DOM アクセス"]
component --> jsx
component --> props
component --> events
component --> ref
style component fill:#673ab8,color:#fff
これらの要素を組み合わせることで、インタラクティブな UI を構築できます。本記事では、これら 4 つの要素の具体的な書き方を順に解説していきますね。
課題
Preact 開発における典型的な課題
Preact を使い始めた開発者が直面する課題は、主に以下の 3 点に集約されます。
1. JSX の記法バリエーション
JSX には条件分岐、ループ、フラグメントなど、複数の記法があります。どの場面でどの書き方を使うべきか、混乱しやすいポイントです。
2. Props の型安全性と受け渡し
TypeScript を使う場合、Props の型定義や、デフォルト値の設定、分割代入の使い方など、適切なパターンを知っておく必要があります。
3. イベントハンドリングと DOM 操作
イベントハンドラの書き方、カスタムイベントの実装、直接 DOM を操作する Ref の使い方など、実装パターンが多岐にわたるため、迷いやすいでしょう。
以下の図は、これらの課題の関係性を示しています。
mermaidflowchart TB
start["Preact 開発開始"]
issue1["JSX の記法<br/>どう書くべき?"]
issue2["Props の型定義<br/>どう設計する?"]
issue3["Event / Ref<br/>どう実装する?"]
result["開発の停滞<br/>検索時間の増加"]
start --> issue1
start --> issue2
start --> issue3
issue1 --> result
issue2 --> result
issue3 --> result
style result fill:#ff5252,color:#fff
これらの課題を解決するには、各機能の書き方を体系的に整理し、すぐに参照できる形でまとめておくことが重要です。
解決策
チートシート形式での体系的整理
本記事では、Preact の主要な書き方を以下の 4 つのカテゴリに分類し、それぞれ具体的なコード例とともに解説します。
mermaidflowchart LR
cheatsheet["Preact<br/>チートシート"]
jsx_section["JSX セクション<br/>UI 記述"]
props_section["Props セクション<br/>データ受け渡し"]
events_section["Events セクション<br/>イベント処理"]
ref_section["Ref セクション<br/>DOM 操作"]
cheatsheet --> jsx_section
cheatsheet --> props_section
cheatsheet --> events_section
cheatsheet --> ref_section
style cheatsheet fill:#673ab8,color:#fff
各セクションは独立しているため、必要な部分だけを参照できます。それでは、順番に見ていきましょう。
具体例
JSX の書き方
JSX は Preact で UI を記述するための構文です。HTML に似た記法で、JavaScript の中に直接マークアップを書くことができます。
基本的な JSX
最もシンプルな JSX の書き方です。
typescriptimport { h } from 'preact';
// シンプルな要素
const SimpleElement = () => {
return <div>Hello, Preact!</div>;
};
h
関数をインポートすることで、JSX を使えるようになります。h
は Hyperscript の略で、仮想 DOM を生成する関数です。
属性とプロパティ
HTML 属性や DOM プロパティを JSX で指定する方法です。
typescript// 属性の設定
const ElementWithAttributes = () => {
return (
<div
className='container' // class は className
id='main'
data-test='value' // data 属性
>
<input
type='text'
placeholder='Enter text'
disabled={false} // 真偽値属性
/>
</div>
);
};
注意点として、HTML の class
属性は JSX では className
として記述します。これは JavaScript の予約語との衝突を避けるためです。
スタイルの適用
インラインスタイルを適用する場合、オブジェクト形式で記述します。
typescript// インラインスタイル
const StyledElement = () => {
const styles = {
color: 'blue',
fontSize: '16px', // キャメルケースで記述
backgroundColor: '#f0f0f0',
};
return <div style={styles}>スタイル付きテキスト</div>;
};
CSS プロパティ名はキャメルケース(例:backgroundColor
)で記述し、値は文字列で指定します。
条件付きレンダリング
条件によって表示内容を切り替える方法です。
typescript// 三項演算子
const ConditionalRender = ({
isLoggedIn,
}: {
isLoggedIn: boolean;
}) => {
return (
<div>
{isLoggedIn ? (
<p>ログイン済み</p>
) : (
<p>ログインしてください</p>
)}
</div>
);
};
三項演算子を使うことで、簡潔に条件分岐を表現できます。
typescript// 論理 AND 演算子
const ConditionalRenderAnd = ({
showMessage,
}: {
showMessage: boolean;
}) => {
return (
<div>{showMessage && <p>メッセージを表示</p>}</div>
);
};
&&
演算子を使えば、条件が真のときのみ要素を表示できますね。
リストのレンダリング
配列データをループして表示する方法です。
typescript// map を使ったリスト
const ListRender = () => {
const items = ['Apple', 'Banana', 'Orange'];
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li> // key 属性は必須
))}
</ul>
);
};
map
メソッドで配列をループし、各要素に key
属性を付与します。key
は Preact が要素を識別するために使われます。
typescript// オブジェクト配列のリスト
interface User {
id: number;
name: string;
}
const UserList = ({ users }: { users: User[] }) => {
return (
<ul>
{users.map((user) => (
<li key={user.id}>
{' '}
// ユニークな ID を key に{user.name}
</li>
))}
</ul>
);
};
オブジェクトの場合、ユニークな ID を key
に使用するのがベストプラクティスです。
フラグメント
複数の要素をグループ化する際、不要な DOM ノードを作らない方法です。
typescriptimport { Fragment } from 'preact';
// Fragment を使った記法
const FragmentExample = () => {
return (
<Fragment>
<h1>タイトル</h1>
<p>本文</p>
</Fragment>
);
};
Fragment
を使うと、余計な div
などを追加せずに複数要素を返せます。
typescript// 短縮記法
const FragmentShorthand = () => {
return (
<>
<h1>タイトル</h1>
<p>本文</p>
</>
);
};
<>...</>
という短縮記法も使えますが、key
属性が必要な場合は Fragment
を使います。
JSX セクションのまとめ(図で理解できる要点)
- JSX は
h
関数をベースに仮想 DOM を生成する - 条件分岐やループは JavaScript の式として記述できる
Fragment
を使えば不要な DOM ノードを削減できる
Props の書き方
Props はコンポーネント間でデータを受け渡すための仕組みです。親コンポーネントから子コンポーネントへ、一方向にデータが流れます。
基本的な Props
最もシンプルな Props の受け渡し方法です。
typescript// 親コンポーネント
const Parent = () => {
return <Child message='Hello from parent' />;
};
// 子コンポーネント
interface ChildProps {
message: string;
}
const Child = (props: ChildProps) => {
return <div>{props.message}</div>;
};
TypeScript を使う場合、Props の型を定義することで型安全性が高まります。
分割代入を使った Props
Props を分割代入で受け取ることで、コードがより簡潔になります。
typescript// 分割代入
interface ButtonProps {
label: string;
disabled?: boolean; // オプショナル
}
const Button = ({ label, disabled }: ButtonProps) => {
return <button disabled={disabled}>{label}</button>;
};
関数の引数で直接分割代入すると、props.label
ではなく label
と書けるため、可読性が向上します。
デフォルト値の設定
Props にデフォルト値を設定する方法です。
typescript// デフォルト値(方法1:分割代入)
interface CounterProps {
initialCount?: number;
}
const Counter = ({ initialCount = 0 }: CounterProps) => {
return <div>Count: {initialCount}</div>;
};
分割代入の際に =
でデフォルト値を指定できます。
typescript// デフォルト値(方法2:defaultProps)
const Counter2 = (props: CounterProps) => {
return <div>Count: {props.initialCount}</div>;
};
Counter2.defaultProps = {
initialCount: 0,
};
defaultProps
を使う方法もありますが、TypeScript では分割代入の方が型推論が効きやすいでしょう。
Children Props
子要素を Props として受け取る方法です。
typescriptimport { ComponentChildren } from 'preact';
// children を受け取る
interface CardProps {
title: string;
children: ComponentChildren;
}
const Card = ({ title, children }: CardProps) => {
return (
<div className='card'>
<h2>{title}</h2>
<div className='content'>{children}</div>
</div>
);
};
children
は特別な Props で、コンポーネントタグの間に書かれた内容が渡されます。
typescript// Card の使用例
const App = () => {
return (
<Card title='タイトル'>
<p>カードの内容</p>
<p>複数の要素も OK</p>
</Card>
);
};
このように、柔軟なコンポーネント設計ができますね。
関数を Props として渡す
イベントハンドラなどの関数を Props として渡す方法です。
typescript// 関数を Props として定義
interface SearchBoxProps {
onSearch: (query: string) => void;
}
const SearchBox = ({ onSearch }: SearchBoxProps) => {
const handleSubmit = (e: Event) => {
e.preventDefault();
const input = (e.target as HTMLFormElement).query;
onSearch(input.value);
};
return (
<form onSubmit={handleSubmit}>
<input type='text' name='query' />
<button type='submit'>検索</button>
</form>
);
};
関数の型は (引数の型) => 戻り値の型
という形式で定義します。
typescript// 親コンポーネントでの使用
const ParentComponent = () => {
const handleSearch = (query: string) => {
console.log('検索:', query);
};
return <SearchBox onSearch={handleSearch} />;
};
親から子へコールバック関数を渡すことで、子から親へデータを伝えられます。
スプレッド演算子での Props 展開
複数の Props をまとめて渡す方法です。
typescript// スプレッド演算子
interface InputProps {
type: string;
placeholder: string;
required: boolean;
}
const Input = (props: InputProps) => {
return <input {...props} />; // すべての Props を展開
};
// 使用例
const Form = () => {
const inputProps: InputProps = {
type: 'email',
placeholder: 'メールアドレス',
required: true,
};
return <Input {...inputProps} />;
};
{...props}
とすることで、オブジェクトのすべてのプロパティを展開して渡せます。
Props のバリデーション
Props の型チェックをより厳密に行う方法です。
typescript// Union 型
interface StatusProps {
status: 'success' | 'error' | 'warning';
}
const StatusMessage = ({ status }: StatusProps) => {
const colors = {
success: 'green',
error: 'red',
warning: 'orange',
};
return (
<div style={{ color: colors[status] }}>
Status: {status}
</div>
);
};
Union 型を使うことで、特定の値のみを許可できます。
Props セクションのまとめ(図で理解できる要点)
- Props は親から子への一方向データフローを実現する
- TypeScript の型定義で Props の安全性を高められる
- 分割代入やデフォルト値を使うことでコードが簡潔になる
Events の書き方
Preact では、DOM イベントをリスナーとして登録し、ユーザーの操作に応答します。
基本的なイベントハンドリング
クリックイベントなど、基本的なイベントの処理方法です。
typescript// onClick イベント
const ClickButton = () => {
const handleClick = () => {
console.log('クリックされました');
};
return <button onClick={handleClick}>クリック</button>;
};
イベントハンドラは on + イベント名(キャメルケース)
という形式で指定します。
typescript// インラインでイベントハンドラを定義
const InlineEventButton = () => {
return (
<button
onClick={() => console.log('インラインクリック')}
>
クリック
</button>
);
};
シンプルな処理であれば、インラインで定義することもできますね。
イベントオブジェクト
イベントハンドラには自動的にイベントオブジェクトが渡されます。
typescript// イベントオブジェクトの利用
const EventObjectExample = () => {
const handleClick = (e: MouseEvent) => {
console.log('クリック位置:', e.clientX, e.clientY);
// デフォルト動作を防ぐ
e.preventDefault();
};
return (
<a href='#' onClick={handleClick}>
リンク
</a>
);
};
preventDefault()
でデフォルト動作を防いだり、stopPropagation()
でイベントの伝播を止めたりできます。
フォームイベント
フォームの送信や入力変更を処理する方法です。
typescriptimport { useState } from 'preact/hooks';
// フォーム送信
const FormSubmit = () => {
const [value, setValue] = useState('');
const handleSubmit = (e: Event) => {
e.preventDefault(); // ページリロードを防ぐ
console.log('送信:', value);
};
return (
<form onSubmit={handleSubmit}>
<input
type='text'
value={value}
onInput={(e) =>
setValue((e.target as HTMLInputElement).value)
}
/>
<button type='submit'>送信</button>
</form>
);
};
onSubmit
でフォーム送信を、onInput
で入力変更を処理します。
typescript// Change イベント
const ChangeEvent = () => {
const [selected, setSelected] = useState('');
const handleChange = (e: Event) => {
const value = (e.target as HTMLSelectElement).value;
setSelected(value);
};
return (
<select onChange={handleChange} value={selected}>
<option value=''>選択してください</option>
<option value='1'>オプション1</option>
<option value='2'>オプション2</option>
</select>
);
};
onChange
はセレクトボックスやチェックボックスでよく使われます。
キーボードイベント
キーボード操作を処理する方法です。
typescript// キーボードイベント
const KeyboardEvent = () => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Enter') {
console.log('Enter が押されました');
} else if (e.key === 'Escape') {
console.log('Escape が押されました');
}
};
return (
<input
type='text'
onKeyDown={handleKeyDown}
placeholder='キーを押してみてください'
/>
);
};
e.key
でどのキーが押されたかを判定できます。
マウスイベント
マウスの動きや状態を処理する方法です。
typescript// マウスイベント
const MouseEvents = () => {
const handleMouseEnter = () => {
console.log('マウスが入りました');
};
const handleMouseLeave = () => {
console.log('マウスが出ました');
};
const handleMouseMove = (e: MouseEvent) => {
console.log('マウス位置:', e.clientX, e.clientY);
};
return (
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onMouseMove={handleMouseMove}
style={{
width: '200px',
height: '200px',
border: '1px solid black',
}}
>
ホバーエリア
</div>
);
};
onMouseEnter
、onMouseLeave
、onMouseMove
などのイベントでマウス操作を検出できます。
カスタムイベント
コンポーネント間でカスタムイベントを発火する方法です。
typescript// カスタムイベントを発火する子コンポーネント
interface CustomEventChildProps {
onCustomEvent: (data: { message: string }) => void;
}
const CustomEventChild = ({
onCustomEvent,
}: CustomEventChildProps) => {
const handleClick = () => {
// カスタムイベントを発火
onCustomEvent({ message: 'カスタムデータ' });
};
return (
<button onClick={handleClick}>イベント発火</button>
);
};
Props として関数を受け取り、その関数を呼び出すことでカスタムイベントを実装できます。
typescript// 親コンポーネントでカスタムイベントを受け取る
const CustomEventParent = () => {
const handleCustomEvent = (data: { message: string }) => {
console.log('受信:', data.message);
};
return (
<CustomEventChild onCustomEvent={handleCustomEvent} />
);
};
この仕組みにより、子から親へのデータ送信が可能になりますね。
イベントの委譲
複数の要素のイベントを親要素でまとめて処理する方法です。
typescript// イベント委譲
const EventDelegation = () => {
const handleClick = (e: MouseEvent) => {
const target = e.target as HTMLElement;
// クリックされた要素が button の場合
if (target.tagName === 'BUTTON') {
console.log(
'ボタンがクリックされました:',
target.textContent
);
}
};
return (
<div onClick={handleClick}>
<button>ボタン1</button>
<button>ボタン2</button>
<button>ボタン3</button>
</div>
);
};
親要素でイベントを受け取ることで、各子要素にハンドラを設定する必要がなくなります。
Events セクションのまとめ(図で理解できる要点)
- イベントハンドラは
on + イベント名
の形式で指定する - イベントオブジェクトを使って詳細な情報にアクセスできる
- カスタムイベントは Props の関数を通じて実装する
Ref の書き方
Ref は DOM 要素やコンポーネントインスタンスへの直接参照を保持する仕組みです。
基本的な Ref の使い方
useRef
フックを使って Ref を作成します。
typescriptimport { useRef } from 'preact/hooks';
// 基本的な Ref
const BasicRef = () => {
const inputRef = useRef<HTMLInputElement>(null);
const handleClick = () => {
// Ref 経由で DOM にアクセス
if (inputRef.current) {
inputRef.current.focus(); // フォーカスを当てる
}
};
return (
<div>
<input ref={inputRef} type='text' />
<button onClick={handleClick}>フォーカス</button>
</div>
);
};
useRef
で作成した Ref オブジェクトを、要素の ref
属性に渡します。実際の DOM 要素には ref.current
でアクセスできます。
Ref で DOM を操作する
Ref を使って DOM を直接操作する例です。
typescript// DOM 操作
const DOMManipulation = () => {
const divRef = useRef<HTMLDivElement>(null);
const changeColor = () => {
if (divRef.current) {
divRef.current.style.backgroundColor = 'lightblue';
}
};
const changeText = () => {
if (divRef.current) {
divRef.current.textContent =
'テキストが変更されました';
}
};
return (
<div>
<div ref={divRef}>元のテキスト</div>
<button onClick={changeColor}>色を変更</button>
<button onClick={changeText}>テキストを変更</button>
</div>
);
};
通常は State を使って UI を更新しますが、アニメーションやサードパーティライブラリとの統合など、直接 DOM 操作が必要な場面で Ref が活躍します。
複数の Ref を管理する
配列やオブジェクトで複数の Ref を管理する方法です。
typescript// 複数の Ref(配列)
const MultipleRefs = () => {
const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
const focusItem = (index: number) => {
if (itemRefs.current[index]) {
itemRefs.current[index]?.scrollIntoView({
behavior: 'smooth',
});
}
};
const items = [
'アイテム1',
'アイテム2',
'アイテム3',
'アイテム4',
];
return (
<div>
<ul>
{items.map((item, index) => (
<li
key={index}
ref={(el) => (itemRefs.current[index] = el)}
>
{item}
</li>
))}
</ul>
<button onClick={() => focusItem(2)}>
3番目にスクロール
</button>
</div>
);
};
ref
属性にコールバック関数を渡すことで、動的に Ref を設定できます。
Ref のコールバック形式
Ref が設定・解除されるタイミングで処理を実行する方法です。
typescript// Ref コールバック
const RefCallback = () => {
const setRef = (element: HTMLInputElement | null) => {
if (element) {
console.log('Ref が設定されました');
element.focus();
} else {
console.log('Ref が解除されました');
}
};
return <input ref={setRef} type='text' />;
};
コールバック形式の Ref は、要素がマウントされたときと、アンマウントされたときに呼び出されます。
親コンポーネントから子の Ref にアクセス
forwardRef
を使って、親から子コンポーネントの DOM にアクセスする方法です。
typescriptimport { forwardRef } from 'preact/compat';
import { Ref } from 'preact';
// forwardRef で Ref を転送
interface CustomInputProps {
placeholder?: string;
}
const CustomInput = forwardRef<
HTMLInputElement,
CustomInputProps
>(({ placeholder }, ref) => {
return (
<input
ref={ref}
type='text'
placeholder={placeholder}
/>
);
});
forwardRef
でラップすることで、親から渡された Ref を子の DOM 要素に転送できます。
typescript// 親コンポーネントで使用
const ParentWithForwardRef = () => {
const inputRef = useRef<HTMLInputElement>(null);
const handleFocus = () => {
if (inputRef.current) {
inputRef.current.focus();
}
};
return (
<div>
<CustomInput
ref={inputRef}
placeholder='テキスト入力'
/>
<button onClick={handleFocus}>フォーカス</button>
</div>
);
};
これにより、カプセル化されたコンポーネントでも柔軟に DOM 操作ができますね。
useImperativeHandle で公開メソッドを制御
子コンポーネントが親に公開するメソッドを制御する方法です。
typescriptimport {
useImperativeHandle,
forwardRef,
} from 'preact/compat';
import { useRef } from 'preact/hooks';
// 公開するメソッドの型
interface VideoPlayerRef {
play: () => void;
pause: () => void;
}
// useImperativeHandle の使用
const VideoPlayer = forwardRef<VideoPlayerRef, {}>(
(props, ref) => {
const videoRef = useRef<HTMLVideoElement>(null);
// 親に公開するメソッドを定義
useImperativeHandle(ref, () => ({
play: () => {
videoRef.current?.play();
},
pause: () => {
videoRef.current?.pause();
},
}));
return <video ref={videoRef} src='video.mp4' />;
}
);
useImperativeHandle
を使うことで、内部実装を隠蔽しつつ、必要なメソッドだけを公開できます。
typescript// 親コンポーネントでの使用
const VideoPlayerParent = () => {
const playerRef = useRef<VideoPlayerRef>(null);
return (
<div>
<VideoPlayer ref={playerRef} />
<button onClick={() => playerRef.current?.play()}>
再生
</button>
<button onClick={() => playerRef.current?.pause()}>
停止
</button>
</div>
);
};
カプセル化されたコンポーネントの設計に役立ちますね。
Ref の使用上の注意点
Ref を使う際の重要なポイントをまとめます。
# | 注意点 | 説明 |
---|---|---|
1 | State との使い分け | UI の更新には State、DOM 操作には Ref を使う |
2 | null チェック | ref.current は null の可能性があるため、必ずチェックする |
3 | レンダリング中の使用禁止 | Ref の値はレンダリング中に変更してはいけない |
4 | 依存配列に含めない | useEffect の依存配列に Ref を含めても再実行されない |
typescript// Ref の適切な使用例
const ProperRefUsage = () => {
const buttonRef = useRef<HTMLButtonElement>(null);
const [count, setCount] = useState(0);
// ✅ イベントハンドラ内で使用
const handleClick = () => {
if (buttonRef.current) {
buttonRef.current.style.backgroundColor = 'green';
}
setCount(count + 1);
};
// ❌ レンダリング中に使用してはいけない
// if (buttonRef.current) {
// buttonRef.current.textContent = 'NG';
// }
return (
<button ref={buttonRef} onClick={handleClick}>
クリック数: {count}
</button>
);
};
Ref は強力な機能ですが、使いすぎると宣言的な UI の利点が失われるため、本当に必要な場面でのみ使いましょう。
Ref セクションのまとめ(図で理解できる要点)
- Ref は DOM への直接アクセスを可能にする
forwardRef
で親から子の DOM にアクセスできるuseImperativeHandle
で公開メソッドを制御できる- State と Ref を適切に使い分けることが重要
まとめ
本記事では、Preact の基本的な書き方を JSX、Props、Events、Ref の 4 つのカテゴリに分けて解説しました。
各機能のポイント
JSX では、条件分岐やループ、フラグメントなど、UI を記述するための様々な記法を紹介しました。HTML に似た直感的な構文で、JavaScript の力を活用できるのが特徴です。
Props では、コンポーネント間のデータ受け渡し方法を学びました。TypeScript の型定義を活用することで、安全で保守性の高いコードが書けます。
Events では、ユーザー操作に応答するための様々なイベントハンドリング方法を解説しました。基本的なクリックイベントから、カスタムイベントまで、柔軟に対応できます。
Ref では、DOM への直接アクセス方法を紹介しました。State と Ref を適切に使い分けることで、宣言的な UI と命令的な操作のバランスを取ることができるでしょう。
チートシートとしての活用方法
本記事は保存版として、以下のような場面で参照してください。
# | 場面 | 参照セクション |
---|---|---|
1 | 条件付きレンダリングの書き方を忘れた | JSX の書き方 |
2 | Props の型定義方法を確認したい | Props の書き方 |
3 | イベントハンドラの記法を確認したい | Events の書き方 |
4 | DOM を直接操作したい | Ref の書き方 |
Preact の開発において、これらの基本的な書き方をマスターすることで、より効率的にアプリケーションを構築できます。本記事が皆さんの開発の助けになれば幸いです。
関連リンク
- article
Preact チートシート【保存版】:JSX/Props/Events/Ref の書き方早見表
- article
Vite で始める Preact:公式プラグイン設定と最短プロジェクト作成【完全手順】
- article
【徹底比較】Preact vs React 2025:バンドル・FPS・メモリ・DX を総合評価
- article
【2025 年最新版】Preact の強みと限界を実測で俯瞰:軽量・高速・互換性の現在地
- article
既存 React プロジェクトを Preact に移行する完全ロードマップ
- article
ゼロから始める Preact 開発 - セットアップから初回デプロイまで
- article
Preact チートシート【保存版】:JSX/Props/Events/Ref の書き方早見表
- article
Playwright コマンド&テストランナー チートシート【保存版スニペット集】
- article
htmx 属性チートシート:hx-get/hx-post/hx-swap/hx-target 早見表【実例付き】
- article
Homebrew コマンドチートシート 2025:毎日使う 60 コマンド即参照リスト
- article
Node.js クリーンアーキテクチャ実践:アダプタ/ユースケース/エンティティの分離
- article
gpt-oss のモデルルーティング設計:サイズ別・ドメイン別・コスト別の自動切替
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来