概要
MDNのReact ToDo リストをはじめるを参考にQwikで実装してみます。
実装
プロジェクトの初めのApp()とCSSを配置します。
routes/index.tsx の内容をプロジェクトの出発地点のコードに置き換え
global.cssに用意されたスタイルに置き換えます。
そのままは使用できず、以下に置き換える必要があります。
htmlFor → for
ReactではforがJavaScriptの予約語であるために、代わりにhtmlForを使用するようになっていますがQwikではHTMLプロパティとして記述出来るようです。
defaultChecked={true} → checked
ReactではdefaultCheckedを使用することで、最初にマウントされた時のチェックボックスの状態を設定できますが、一旦checkedにします。
起動すると以下のようにTodoアプリのフレームが表示されます。
Task部分をコンポーネントとして切り出す
import { component$ } from "@builder.io/qwik";
type Todo = {
id: string
name: string
completed: boolean
}
export default component$((props: Todo) => {
return (
<li className="todo stack-small">
<div className="c-cb">
<input id={props.id} type="checkbox" checked={props.completed} />
<label className="todo-label" for="todo-0">
{props.name}
</label>
</div>
<div className="btn-group">
<button type="button" className="btn">
Edit <span className="visually-hidden">Eat</span>
</button>
<button type="button" className="btn btn__danger">
Delete <span className="visually-hidden">Eat</span>
</button>
</div>
</li>
)
})
index.tsxのリスト部分を先程作成したTodoコンポーネントに書き換えます。
~~~~~~
<Todo name='Eat' completed={true} id="todo-0"/>
<Todo name='Sleep' completed={true} id="todo-1"/>
<Todo name='Repeat' completed={true} id="todo-2"/>
~~~~~~
データを反復してレンダリングする
DATAをcomponentの外に書いてしまうと eslint(rules/valid-lexical-scope)に怒られてしまうのでcomponentの中に書いていきます。
あとはtaskListにDATAのmapでTodoコンポーネントの配列を代入してそれをDOMに追加することで表示できます。
export default component$(() => {
const DATA = [
{ id: "todo-0", name: "Eat", completed: true },
{ id: "todo-1", name: "Sleep", completed: false },
{ id: "todo-2", name: "Repeat", completed: false },
];
const taskList = DATA.map((task) => (
<Todo id={task.id} name={task.name} completed={task.completed} />
));
return (
~~~~~~
<ul
role="list"
className="todo-list stack-large stack-exception"
aria-labelledby="list-heading"
>
{taskList}
</ul>
~~~~~~
アプリの残り部分をコンポーネント化
タスク表示以外の部分をコンポーネント化していきます。
Formコンポーネント
import { component$ } from "@builder.io/qwik";
export default component$(() => {
return (
<form>
<h2 className="label-wrapper">
<label for="new-todo-input" className="label__lg">
What needs to be done?
</label>
</h2>
<input
type="text"
id="new-todo-input"
className="input input__lg"
name="text"
autoComplete="off"
/>
<button type="submit" className="btn btn__primary btn__lg">
Add
</button>
</form>
)
})
FilterButtonコンポーネント
import { component$ } from "@builder.io/qwik";
type FilterButton = {
category: string
}
export default component$((props: FilterButton) => {
return (
<button type="button" className="btn toggle-btn" aria-pressed="true">
<span className="visually-hidden">Show </span>
<span>{props.category}</span>
<span className="visually-hidden"> tasks</span>
</button>
)
})
ルートのindex.tsxファイル
import { component$ } from '@builder.io/qwik';
import type { DocumentHead } from '@builder.io/qwik-city';
import FilterButton from '~/components/filterButton';
import Form from '~/components/form';
import Todo from '~/components/todo';
export default component$(() => {
const DATA = [
{ id: "todo-0", name: "Eat", completed: true },
{ id: "todo-1", name: "Sleep", completed: false },
{ id: "todo-2", name: "Repeat", completed: false },
];
const taskList = DATA.map((task) => (
<Todo id={task.id} name={task.name} completed={task.completed} />
));
return (
<div className="todoapp stack-large">
<h1>TodoMatic</h1>
<Form />
<div className="filters btn-group stack-exception">
<FilterButton category='all' />
<FilterButton category='Active' />
<FilterButton category='Completed' />
</div>
<h2 id="list-heading">
3 tasks remaining
</h2>
<ul
role="list"
className="todo-list stack-large stack-exception"
aria-labelledby="list-heading"
>
{taskList}
</ul>
</div>
);
});
export const head: DocumentHead = {
title: 'Welcome to Qwik',
meta: [
{
name: 'description',
content: 'Qwik site description',
},
],
};
ここまでで、ファイル分割ができました。
アプリを起動すると、見た目は変わりませんが以下のようにTodoアプリが表示されるはずです。
明日も引き続き完成を目指してやっていきます。