QwikでTODOアプリを作ってみる（1）


# 概要
MDNのReact ToDo リストをはじめるを参考にQwikで実装してみます。

https://developer.mozilla.org/ja/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning

# 実装
## プロジェクトの初めのApp()とCSSを配置します。

routes/index.tsx の内容をプロジェクトの出発地点のコードに置き換え
global.cssに用意されたスタイルに置き換えます。

そのままは使用できず、以下に置き換える必要があります。

```
htmlFor → for
```

ReactではforがJavaScriptの予約語であるために、代わりにhtmlForを使用するようになっていますがQwikではHTMLプロパティとして記述出来るようです。


```
defaultChecked={true} → checked
```

ReactではdefaultCheckedを使用することで、最初にマウントされた時のチェックボックスの状態を設定できますが、一旦checkedにします。

起動すると以下のようにTodoアプリのフレームが表示されます。

![スクリーンショット 2022-11-27 22.51.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154845/c55dd7ce-428a-1b13-1056-6836ee929630.png)

## Task部分をコンポーネントとして切り出す

```src/components/todo/index.tsx
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コンポーネントに書き換えます。

```src/routes/index.tsx
~~~~~~
<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に追加することで表示できます。

```src/routes/index.tsx
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コンポーネント
```src/components/form/index.tsx
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コンポーネント

```src/components/filterButton/index.tsx
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ファイル
```src/routes/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アプリが表示されるはずです。

![スクリーンショット 2022-12-02 11.17.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154845/92d00d03-11cc-8b26-e65f-3d95ec7f5666.png)


明日も引き続き完成を目指してやっていきます。