react-big-calendarを学ぶ(Step1:ドラッグアンドドロップ)

## はじめに
react-big-calendarについて学んでいきます！

https://github.com/jquense/react-big-calendar

## 成果物

![react-big-calendar.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/8ba361d0-e01a-4226-994f-8298b0dc8443.gif)

## ソースコード
```shell:
~/develop/my-calendar-app  (main)$ tree -I node_modules/
.
├── package-lock.json
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── README.md
├── src
│   ├── App.css
│   ├── App.test.tsx
│   ├── App.tsx
│   ├── declarations.d.ts
│   ├── index.css
│   ├── index.tsx
│   ├── logo.svg
│   ├── react-app-env.d.ts
│   ├── reportWebVitals.ts
│   └── setupTests.ts
└── tsconfig.json

3 directories, 20 files
```

```json:package.json
{
  "name": "my-calendar-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/dom": "^10.4.0",
    "@testing-library/jest-dom": "^6.6.3",
    "@testing-library/react": "^16.3.0",
    "@testing-library/user-event": "^13.5.0",
    "@types/jest": "^27.5.2",
    "@types/node": "^16.18.126",
    "@types/react": "^19.1.4",
    "@types/react-dom": "^19.1.5",
    "date-fns": "^4.1.0",
    "react": "^19.1.0",
    "react-big-calendar": "^1.18.0",
    "react-dnd": "^16.0.1",
    "react-dnd-html5-backend": "^16.0.1",
    "react-dom": "^19.1.0",
    "react-scripts": "5.0.1",
    "typescript": "^4.9.5",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

```

```tsx:src/App.tsx
import { useState } from "react";
import { Calendar, dateFnsLocalizer } from "react-big-calendar";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import withDragAndDrop from "react-big-calendar/lib/addons/dragAndDrop";
import { format, parse, startOfWeek, getDay } from "date-fns";
import { ja } from "date-fns/locale";

import "react-big-calendar/lib/css/react-big-calendar.css";
import "react-big-calendar/lib/addons/dragAndDrop/styles.css";

// DnD対応カレンダーに拡張
const DragAndDropCalendar = withDragAndDrop(Calendar);

// date-fns ローカライザー設定
const locales = {
	ja: ja,
};

const localizer = dateFnsLocalizer({
	format,
	parse,
	startOfWeek,
	getDay,
	locales,
	locale: "ja",
});

type EventType = {
	id: number;
	title: string;
	start: Date;
	end: Date;
};

const initialEvents: EventType[] = [
	{
		id: 0,
		title: "会議A",
		start: new Date(),
		end: new Date(new Date().getTime() + 60 * 60 * 1000), // +1時間
	},
];

type EventDropArgs = {
	event: EventType;
	start: Date;
	end: Date;
};

export default function App() {
	const [events, setEvents] = useState<EventType[]>(initialEvents);

	const onEventDrop = ({ event, start, end }: EventDropArgs) => {
		const updated = events.map((e) =>
			e.id === event.id ? { ...e, start, end } : e,
		);
		setEvents(updated);
	};

	return (
		<DndProvider backend={HTML5Backend}>
			<div style={{ height: "100vh", padding: 20 }}>
				<DragAndDropCalendar
					localizer={localizer}
					events={events}
					startAccessor="start"
					endAccessor="end"
					onEventDrop={onEventDrop}
					resizable
					onEventResize={onEventDrop}
					style={{ height: "100%" }}
				/>
			</div>
		</DndProvider>
	);
}


```