🦧

ChatGPT先生にNext.jsでTODOアプリの作り方を教わる

2023/03/03に公開

プロンプトにて

todo application in nextjs

すべきことを教えてくれた

いつものように「Sure!, Here's an example of ...」とコードを吐き出してくれる。
まずは以下のコマンドを叩いてね。

npx create-next-app my-todo-app
cd my-todo-app
npm run dev

そして [pages/index.tsx」を以下にまるっと書き換えます。

index.tsx
import { useState } from 'react'

export default function Home() {
  const [todos, setTodos] = useState([])
  const [inputValue, setInputValue] = useState('')

  const handleAddTodo = () => {
    if (!inputValue) return
    setTodos([...todos, inputValue])
    setInputValue('')
  }

  const handleDeleteTodo = (index) => {
    setTodos(todos.filter((_, i) => i !== index))
  }

  return (
    <div>
      <h1>Todo App</h1>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <button onClick={handleAddTodo}>Add Todo</button>
      <ul>
        {todos.map((todo, index) => (
          <li key={index}>
            {todo}
            <button onClick={() => handleDeleteTodo(index)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  )
}

手直し

そのままだとTSによるエラーが残る

  • useStateの型推論によるエラー
const todos: never[]
Type 'string' is not assignable to type 'never'.ts(2322)
  • 型の不明記
(parameter) index: any
Parameter 'index' implicitly has an 'any' type.

TS対応版を知りたい

ということでもう一度ChatGPTに聞いてみましょう。

what about along with typescript?

そしたら以下のコードを教え得てくれます

型明記アリのTSXコード
import { useState } from 'react'

type Todo = {
  id: number
  text: string
}

export default function Home() {
  const [todos, setTodos] = useState<Todo[]>([])
  const [inputValue, setInputValue] = useState('')

  const handleAddTodo = () => {
    if (!inputValue) return
    const newTodo: Todo = {
      id: new Date().getTime(),
      text: inputValue,
    }
    setTodos([...todos, newTodo])
    setInputValue('')
  }

  const handleDeleteTodo = (id: number) => {
    setTodos(todos.filter((todo) => todo.id !== id))
  }

  return (
    <div>
      <h1>Todo App</h1>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <button onClick={handleAddTodo}>Add Todo</button>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            {todo.text}
            <button onClick={() => handleDeleteTodo(todo.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  )
}

デザイン追加

このままだと味気ないので「Meterial UI」を使ったらどうなるでしょうか。
https://mui.com/

npm install @mui/material @mui/icons-material @emotion/styled @emotion/react

ChatGPTには以下のように聞いてデザイン付きのコードを吐いてもらいます。

if with dependency
npm install @mui/icons-material @mui/material @emotion/styled @emotion/react
デザイン付きindex.tsx
import { useState } from 'react'
import { styled } from '@mui/material/styles'
import { Button, TextField, List, ListItem, ListItemText, ListItemSecondaryAction, IconButton } from '@mui/material'
import { Delete } from '@mui/icons-material'

type Todo = {
  id: number
  text: string
}

const Root = styled('div')({
  margin: 16,
})

const Input = styled(TextField)({
  marginRight: 16,
})

export default function Home() {
  const [todos, setTodos] = useState<Todo[]>([])
  const [inputValue, setInputValue] = useState('')

  const handleAddTodo = () => {
    if (!inputValue) return
    const newTodo: Todo = {
      id: new Date().getTime(),
      text: inputValue,
    }
    setTodos([...todos, newTodo])
    setInputValue('')
  }

  const handleDeleteTodo = (id: number) => {
    setTodos(todos.filter((todo) => todo.id !== id))
  }

  return (
    <Root>
      <h1>Todo App</h1>
      <Input
        label="Add Todo"
        variant="outlined"
        size="small"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <Button variant="contained" onClick={handleAddTodo}>
        Add
      </Button>
      <List>
        {todos.map((todo) => (
          <ListItem key={todo.id}>
            <ListItemText primary={todo.text} />
            <ListItemSecondaryAction>
              <IconButton edge="end" aria-label="delete" onClick={() => handleDeleteTodo(todo.id)}>
                <Delete />
              </IconButton>
            </ListItemSecondaryAction>
          </ListItem>
        ))}
      </List>
    </Root>
  )
}

最終的な画面

デフォルトのNext.jsのデザインも残っておしゃれなTODOアプリができましたね。

最終的なコード

https://github.com/kentaro-maker/next-todo-typescript-with-chatgpt/tree/main

今後の展開

追加機能は以下を想定しています
-データ保存(クッキー ot ローカルストレージ)
-アカウント別リスト(ログイン)

Discussion