React コンポーネント

React コンポーネントは React アプリケーションを構築する基本単位であり、コンポーネントは再利用可能で独立した UI ユニットと理解できます。各コンポーネントは自身の構造、スタイル、ロジック、状態をカプセル化しています。

React コンポーネントはアプリケーションを構築する基盤であり、コンポーネントはボタン一つ分の小ささからページ全体の大きさまで対応できます。

コンポーネントは次のように分類できます:関数コンポーネントとクラスコンポーネント。

技術的な観点では、React コンポーネントは React 要素(通常は JSX)を返す JavaScript の関数またはクラスです。

// 最简单的组件
function Hello() {
  return <h1>Hello, World!</h1>;
}

複雑な UI をコンポーネントツリーに分割します:

App
├── Header
│   ├── Logo
│   └── Navigation
│       ├── NavItem
│       └── NavItem
├── Main
│   ├── Sidebar
│   └── Content
│       ├── Article
│       └── Article
└── Footer

関数コンポーネント

関数コンポーネントは、コンポーネントを定義する簡潔な方法です。

関数コンポーネントは、props を受け取り React 要素を返す JavaScript の関数です。

基本構文:

// 方式 1:函数声明
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 方式 2:箭头函数
const Welcome = (props) => {
  return <h1>Hello, {props.name}</h1>;
};

// 方式 3:简化写法(单行返回)
const Welcome = (props) => <h1>Hello, {props.name}</h1>;

簡単な関数コンポーネントを作成します:

src/Welcome.js ファイル:

import React from 'react';

// 関数コンポーネントを定義する
function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

export default Welcome;

src/index.js でこのコンポーネントをレンダリングします:

src/index.js ファイル:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Welcome from './Welcome';

const root = ReactDOM.createRoot(document.getElementById("root"));
// Welcome コンポーネントをレンダリングし、name プロパティを渡す
root.render(<Welcome name="World" />);

この例は、name プロパティを受け取り、ページに "Hello, World!" と表示するシンプルなコンポーネントを示しています。


クラスコンポーネント

クラスコンポーネントは ES6 のクラス構文で定義され、通常、状態の管理やライフサイクルメソッドの使用が必要な場合に使われます。

基本構文:

import React, { Component } from 'react';
class Welcome extends Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

クラスコンポーネントを作成します:

実例

import React, { Component } from 'react';

class Welcome extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default Welcome;

src/index.js でこのコンポーネントをレンダリングします:

実例

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Welcome from './Welcome';

const root = ReactDOM.createRoot(document.getElementById("root"));
// Welcome コンポーネントをレンダリングし、name プロパティを渡す
root.render(<Welcome name="World" />);

テスト実例

次に、"Hello World!" を出力するコンポーネントをカプセル化します。コンポーネント名は HelloMessage です:

React 実例

function HelloMessage(props) { return <h1>Hello World!</h1>; } const element = <HelloMessage />; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( element );

試してみる »

実例解析:

1. 関数を使ってコンポーネントを定義できます:

function HelloMessage(props) {
    return <h1>Hello World!</h1>;
}

ES6 のクラスを使ってコンポーネントを定義することもできます:

class Welcome extends React.Component {
  render() {
    return <h1>Hello World!</h1>;
  }
}

2、const element = <HelloMessage />ユーザー定義のコンポーネントです。

注意:ネイティブの HTML 要素名は小文字で始まりますが、カスタムの React クラス名は大文字で始まります。たとえば、HelloMessage を helloMessage と書くことはできません。さらに、コンポーネントクラスはトップレベルのタグを1つだけしか含められないことにも注意してください。そうしないとエラーになります。

コンポーネントにパラメータを渡す必要がある場合は、次を使用できます:this.propsオブジェクト。実例は次のとおりです:

React 実例

function HelloMessage(props) { return <h1>Hello {props.name}!</h1>; } const element = <HelloMessage name="サンプル"/>; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( element );

試してみる »

上記の実例では、name属性はprops.nameを使用して取得します。

注意:属性を追加する際、class 属性は className と書き、for 属性は htmlFor と書く必要があります。これは class と for が JavaScript の予約語であるためです。


複合コンポーネント

複数のコンポーネントを作成して1つのコンポーネントを合成できます。つまり、コンポーネントの異なる機能を分離します。

次の実例では、サイト名と URL を出力するコンポーネントを実装しています:

React 実例

function Name(props) { return <h1>サイト名:{props.name}</h1>; } function Url(props) { return <h1>サイトURL:{props.url}</h1>; } function Nickname(props) { return <h1>サイトのニックネーム:{props.nickname}</h1>; } function App() { return ( <div> <Name name="サンプル" /> <Url url="http://www.example.com" /> <Nickname nickname="サンプル" /> </div> ); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <App /> );

試してみる »

この実例では、App コンポーネントが Name、Url、Nickname コンポーネントを使用して対応する情報を出力しています。


Props(プロパティ)

Props はコンポーネント間でデータを渡す方法であり、関数のパラメータに似ています。

基本の使い方

// 父组件传递 props
function App() {
  return (
    <div>
      <Greeting name="Alice" age={25} />
      <Greeting name="Bob" age={30} />
    </div>
  );
}
// 子组件接收 props
function Greeting(props) {
  return (
    <div>
      <h1>Hello, {props.name}!</h1>
      <p>Age: {props.age}</p>
    </div>
  );
}

Props の分割代入

// 推荐:直接解构
function Greeting({ name, age }) {
  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>Age: {age}</p>
    </div>
  );
}
// 带默认值的解构
function Button({ text = "Submit", variant = "primary", disabled = false }) {
  return (
    <button className={variant} disabled={disabled}>
      {text}
    </button>
  );
}

Props の型

Props は任意の JavaScript の値にできます:

function Demo() {
  const user = { name: "Alice", age: 25 };
  const numbers = [1, 2, 3, 4, 5];
  const handleClick = () => alert("Clicked!");
  return (
    <MyComponent
      // 字符串
      title="Hello"
      // 数字
      count={42}
      // 布尔值
      isActive={true}
      // 数组
      items={numbers}
      // 对象
      user={user}
      // 函数
      onClick={handleClick}
      // JSX
      children={<p>This is content</p>}
    />
  );
}

Children Props

特別な props。コンポーネントの子コンテンツを渡すために使います:

// 方式 1:使用 props.children
function Card(props) {
  return (
    <div className="card">
      <div className="card-body">
        {props.children}
      </div>
    </div>
  );
}
// 方式 2:解构 children
function Card({ children, title }) {
  return (
    <div className="card">
      {title && <h2>{title}</h2>}
      <div className="card-body">{children}</div>
    </div>
  );
}
// 使用
function App() {
  return (
    <Card title="My Card">
      <p>This is the card content</p>
      <button>Click me</button>
    </Card>
  );
}

Props の不変性

重要な原則:props を決して変更してはいけません!

// 错误:修改 props
function BadComponent(props) {
  props.name = "Changed"; // 绝对不要这样做!
  return <h1>{props.name}</h1>;
}
// 正确:将 props 视为只读
function GoodComponent({ name }) {
  const displayName = name.toUpperCase(); // 创建新值
  return <h1>{displayName}</h1>;
}

コンポーネントの合成と再利用

合成パターン

// 基础组件
function Avatar({ src, alt }) {
  return <img src={src} alt={alt} className="avatar" />;
}
function UserInfo({ name, email }) {
  return (
    <div>
      <h3>{name}</h3>
      <p>{email}</p>
    </div>
  );
}
// 组合成复杂组件
function UserCard({ user }) {
  return (
    <div className="user-card">
      <Avatar src={user.avatar} alt={user.name} />
      <UserInfo name={user.name} email={user.email} />
    </div>
  );
}

コンテナコンポーネントパターン

// 容器组件:处理逻辑和状态
function Panel({ title, children, collapsible = false }) {
  const [isOpen, setIsOpen] = useState(true);
  return (
    <div className="panel">
      <div className="panel-header">
        <h2>{title}</h2>
        {collapsible && (
          <button onClick={() => setIsOpen(!isOpen)}>
            {isOpen ? "Collapse" : "Expand"}
          </button>
        )}
      </div>
      {isOpen && (
        <div className="panel-content">
          {children}
        </div>
      )}
    </div>
  );
}
// 使用
function App() {
  return (
    <Panel title="Settings" collapsible>
      <p>Your settings here...</p>
    </Panel>
  );
}

高階コンポーネントの考え方(合成による実装)

// 通用的加载状态包装
function WithLoading({ isLoading, children }) {
  if (isLoading) {
    return <div>Loading...</div>;
  }
  return children;
}
// 使用
function UserList({ users, isLoading }) {
  return (
    <WithLoading isLoading={isLoading}>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </WithLoading>
  );
}

コンポーネント命名規則

命名規則

// 正确:大写字母开头(PascalCase)
function UserProfile() { }
function BlogPost() { }
function NavBar() { }
// 错误:小写字母开头
function userProfile() { } // React 会将其视为 HTML 标签

ファイル命名

// 推荐的文件结构
src/
├── components/
│   ├── Button.jsx          // 或 Button.js
│   ├── UserCard.jsx
│   ├── Navigation/
│   │   ├── Navigation.jsx
│   │   ├── NavItem.jsx
│   │   └── index.js        // 导出 Navigation

コンポーネントのエクスポートとインポート

デフォルトエクスポート

// Button.jsx
export default function Button({ text }) {
  return <button>{text}</button>;
}
// App.jsx
import Button from './Button';

名前付きエクスポート

// components.jsx
export function Button({ text }) {
  return <button>{text}</button>;
}
export function Input({ value, onChange }) {
  return <input value={value} onChange={onChange} />;
}
// App.jsx
import { Button, Input } from './components';

混合エクスポート

// Card.jsx
export default function Card({ children }) {
  return <div className="card">{children}</div>;
}
export function CardHeader({ title }) {
  return <div className="card-header">{title}</div>;
}
export function CardBody({ children }) {
  return <div className="card-body">{children}</div>;
}
// App.jsx
import Card, { CardHeader, CardBody } from './Card';

コンポーネント設計の原則

1. 単一責任の原則

各コンポーネントは1つのことだけを行うべきです。

// 不好:组件做太多事情
function UserDashboard() {
  // 获取用户数据
  // 处理表单
  // 显示图表
  // 处理导航
  // ...
}
// 好:拆分成多个组件
function UserDashboard() {
  return (
    <div>
      <UserProfile />
      <UserStats />
      <UserActivity />
    </div>
  );
}

2. コンポーネントを簡潔に保つ

コンポーネントのコードは 200〜300 行を超えるべきではありません。

// 好:提取子组件
function ProductCard({ product }) {
  return (
    <div className="product-card">
      <ProductImage src={product.image} />
      <ProductInfo name={product.name} price={product.price} />
      <ProductActions productId={product.id} />
    </div>
  );
}

3. Props を適切に使う

渡す props が多すぎないようにします。

// 不好:props 过多
function User({ name, age, email, address, phone, company, role, ... }) { }
// 好:使用对象
function User({ user }) {
  const { name, age, email } = user;
  // ...
}

4. 深いネストを避ける

過度に深いコンポーネントのネストは props drilling 問題を引き起こします。

// 问题:props 需要层层传递
<App>
  <Layout user={user}>
    <Sidebar user={user}>
      <Menu user={user}>
        <MenuItem user={user} />
      </Menu>
    </Sidebar>
  </Layout>
</App>
// 解决:使用 Context 或状态管理
その他の拡張