0
1

Go + Reactでアプリを作成してみた【Todo】

Last updated at Posted at 2024-05-19

technology used

  • Go - Server
  • Fiber - Go web server
  • Vite - Client
  • Mantine - React component library
  • TypeScript - Static types

Serverを作成する

NOTES.md
## Create a Server folder

mkdir Server

## initialize Go app

go mod init github.com/tomdoestech/go-react-todo

## Install Riber v2

go get -u github.com/gofiber/fiver/v2

## Create client app with Vite

yarn create vite client -- --template react-ts

## Install dependencies

yarn add @mantine/hooks @mantine/core swr @primer/octicons-react

①serverフォルダを作成して、initialize Go appする

②server/main.goを作成する

server/main.go
package main

import (
	"fmt"
	"log"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
)

type Todo struct {
	ID int `json:"id"`
	Title string `json:"title"`
	Done bool `json:"done"`
	Body string `json:"body"`
}

func main(){
	fmt.Print("Hello world")

	app := fiber.New()

	app.Use(cors.New(cors.Config{
		AllowOrigins: "http://localhost:3000",
		AllowHeaders: "Origin, Content-Type, Accept",
	}))

	todos := []Todo{}

	app.Get("/healthchecl", func(c *fiber.Ctx) error {
		return c.SendString("OK")
	})

	app.Post("/api/todos", func(c *fiber.Ctx) error {
		todo := &Todo{}

		if err := c.BodyParser(todo); err != nil {
			return err
		}
		todo.ID = len(todos) + 1
		todos = append(todos, *todo)
		return c.JSON(todos)
	})
	app.Patch("/api/todos/:id/done", func(c *fiber.Ctx) error {
		id, err := c.ParamsInt("id")

		if err != nil {
			return c.Status(401).SendString("Invalid id")
		}

		for i, t := range todos {
			if t.ID == id {
				todos[i].Done = true
				break
			}
		}
		return c.JSON(todos)
	})

	app.Get("/api/todos", func(c *fiber.Ctx) error {
		return c.JSON(todos)
	})

	log.Fatal(app.Listen(":4000"))
}

③cd serverとgo run main.goを行う

④postman でエンドポイントを試す

⑤client/src/App.tsxを編集する

client/src/App.tsx
import { Box, List, ThemeIcon } from '@mantine/core';
import { CheckCircleFillIcon } from '@primer/octicons-react';
import useSWR from 'swr';
import './App.css';
import AddTodo from './components/AddTodo';

export interface Todo {
  id: number;
  title: string;
  body: string;
  done: boolean;
}

export const ENDPOINT = 'http://localhost:4000';

const fetcher = (url: string) =>
  fetch(`${ENDPOINT}/${url}`).then((r) => r.json());

function App() {
  const { data, mutate } = useSWR<Todo[]>('api/todos', fetcher);

  async function markTodoAdDone(id: number) {
    const updated = await fetch(`${ENDPOINT}/api/todo/${id}/done`, {
      method: 'PATCH',
    }).then((r) => r.json());
    mutate(updated);
  }

  return (
    <Box
      style={{
        padding: '2rem',
        width: '100%', // また、'windth' は 'width' のタイプミスですので、これも修正しました。
        maxWidth: '40rem',
        margin: '0 auto',
      }}
    >
      <List spacing='xs' size='sm' mb={12} center>
        {data?.map((todo) => {
          return (
            <List.Item
              onClick={() => markTodoAdDone(todo.id)}
              key={`todo_list__${todo.id}`}
            >
              icon=
              {todo.done ? (
                <ThemeIcon color='teal' size={24} radius='xl'>
                  <CheckCircleFillIcon size={20} />
                </ThemeIcon>
              ) : (
                <ThemeIcon color='gray' size={24} radius='xl'>
                  <CheckCircleFillIcon size={20} />
                </ThemeIcon>
              )}
              {todo.title}
            </List.Item>
          );
        })}
      </List>
      <AddTodo mutate={mutate} />
    </Box>
  );
}

export default App;

⑥src/components/AddTodo.tsxを作成する

src/components/AddTodo.tsx
import { useState } from 'react';
import { useForm } from '@mantine/form';
import { Button, Modal, Group, TextInput, Textarea } from '@mantine/core';
import { ENDPOINT } from '../App';
import { KeyedMutator } from 'swr';

function AddTodo({ mutate }: { mutate: KeyedMutator<Todo[]> }) {
  const [open, setOpen] = useState(false);

  const form = useForm({
    initalValues: {
      title: '',
      body: '',
    },
  });

  async function createTodo(values: { title: string; body: string }) {
    const updated = await fetch(`${ENDPOINT}/api/todos`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(values),
    }).then((r) => r.json());

    mutate(updated);
    form.reset();
    setOpen(false);
  }

  return (
    <>
      <Modal opened={open} onClose={() => setOpen(false)} title='Create todo'>
        <form onSubmit={form.onSubmit(createTodo)}>
          <TextInput
            required
            mb={12}
            label='Todo'
            placeholder='What do you want to do?'
            {...form.getInputProps('title')}
          />
          <Textarea
            required
            mb={12}
            label='Body'
            placeholder='Tell me more...'
            {...form.getInputProps('body')}
          />

          <Button type='submit'>Create todo</Button>
        </form>
      </Modal>
      <Group>
        <Button fullWidth mb={12} onClick={() => setOpen(true)}>
          ADD TODO
        </Button>
      </Group>
    </>
  );
}

export default AddTodo;

参考サイト

Build A Go REST API, React.js & TypeScript Todo Application

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1