Reactではオブジェクトの配列をJSX内でループ処理するケースがよくあります。
今回はmap
を利用したループ処理の実装パターンを紹介します。
今回はTodoアプリケーションを例にとり、JSXでのループ処理の方法を説明します。
目次
コンポーネントを分割せず、1つのコンポーネントで完結させる
map
を利用したJSXのループの基本構文は以下の通りです。一番外側の中括弧({}
)はJSX内で式を埋め込むためのものです。
{items.map((item) => {
return (
// JSX
);
})}
Todoリストの各アイテムをmap
によってレンダーする場合は以下のようになります。
TodoApp.tsx
import "./styles.css";
type TodoItem = {
id: number;
content: string;
};
export const TodoApp = () => {
const todos: TodoItem[] = [
{ id: 1, content: "do something" },
{ id: 2, content: "go somewhere" },
];
return (
<div className="todo-app">
<h1>Todo List</h1>
<ul className="todo-list">
{todos.map((todo: TodoItem) => {
return (
<li className="todo-item" key={todo.id}>
<span className="todo-item__text">
{todo.content}
</span>
</li>
);
})}
</ul>
</div>
);
};
export default TodoApp;
ループ処理でレンダーするJSXを別コンポーネントに分割する
map
でレンダーするJSXを別のコンポーネントで定義する方法です。
以下の例ではTodoコンポーネントを新たに作成し、map
内の役割はTodoコンポーネントを呼ぶだけとなっています。
TodoApp.tsx
import "./styles.css";
import Todo from "./components/Todo";
type TodoItem = {
id: number;
content: string;
};
export const TodoApp = () => {
const todos: TodoItem[] = [
{ id: 1, content: "do something" },
{ id: 2, content: "go somewhere" },
];
return (
<div className="todo-app">
<h1>Todo List</h1>
<ul className="todo-list">
{todos.map((todo: TodoItem) => {
return (
<Todo key={todo.id} content={todo.content} />
);
})}
</ul>
</div>
);
};
export default TodoApp;
Todoコンポーネントは以下のようになります。
Todo.tsx
type TodoProps = {
content: String;
};
const Todo: React.FC<TodoProps> = ({ content }) => (
<li className="todo-item">
<span className="todo-item__text">{content}</span>
</li>
);
export default Todo;
なお、アロー関数ではステートメントがreturn
のみの場合、return
およびそれを囲む波括弧({}
)は省略できます。
ですので、コンポーネントを分割する場合は以下のように記述するとシンプルになります。
// Before
{todos.map((todo: TodoItem) => {
return (
<Todo key={todo.id} content={todo.content} />
);
})}
// After
{todos.map((todo: TodoItem) =>
<Todo key={todo.id} content={todo.content} />
)}
アロー関数の省略記法の詳細解説はJavaScriptアロー関数入門。functionからの書き換えや、省略記法について理解するで紹介しています。
ループ処理をするJSXを別コンポーネントに分離する
今回の例でいうと、Todoリストに関するコンポーネントを新たに作成する方法です。
呼び出し元(ここでいうTodoApp.tsx
)からmap
の記述がなくなるため、呼び出し元のコードがシンプルになります。
具体的なコードは以下の通りです。なお、Todoコンポーネントは変更がないため省略します。
TodoApp.tsx
import "./styles.css";
import TodoList from "./components/TodoList";
type TodoItem = {
id: number;
content: string;
};
export const TodoApp = () => {
const todos: TodoItem[] = [
{ id: 1, content: "do something" },
{ id: 2, content: "go somewhere" },
];
return (
<div className="todo-app">
<h1>Todo List</h1>
<TodoList todos={todos}/>
</div>
);
};
export default TodoApp;
TodoList.tsx
import Todo from "./Todo";
type TodoItem = {
id: number;
content: string;
};
type TodoListProps = {
todos: TodoItem[];
};
const TodoList: React.FC<TodoListProps> = ({ todos }) => {
return (
<ul className="todo-list">
{todos.map((todo: TodoItem) =>
<Todo key={todo.id} content={todo.content} />
)}
</ul>
);
};
export default TodoList;
コンポジションモデルを採用する
map
をリスト用のコンポーネント(ここでいうTodoList.tsx
)ではなく、呼び出し元(ここでいうTodoApp.tsx
)に記述する方法です。
リスト用のコンポーネントはchildren
を受け取るようにします。
コンポジションモデルを利用することで、ループ処理されるJSXの内容が呼び出し元のコンポーネントで可視化されます。
またchildren
の情報は事前に決まっていなくてもよいため、リスト用のコンポーネントとして使い回しがしやすくなるというメリットがあります。
以下はコンポジションモデルを採用したコードの例です。なお、Todoコンポーネントは変更がないため省略します。
TodoApp.tsx
import "./styles.css";
import TodoList from "./components/TodoList";
import Todo from "./components/Todo";
type TodoItem = {
id: number;
content: string;
};
export const TodoApp = () => {
const todos: TodoItem[] = [
{ id: 1, content: "do something" },
{ id: 2, content: "go somewhere" },
];
return (
<div className="todo-app">
<h1>Todo List</h1>
<TodoList>
<ul className="todo-list">
{todos.map((todo: TodoItem) => {
return <Todo key={todo.id} content={todo.content} />;
})}
</ul>
</TodoList>
</div>
);
};
export default TodoApp;
TodoList.tsx
export const TodoList: React.FC = ({ children }) => {
return <div>{children}</div>;
};
export default TodoList;
さいごに
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!