React （ TypeScript & Bootstrap ）の構成でアプリを作成しました【Notes Application】

## 環境の準備

#### ターミナルでreactアプリケーションを作成する。

```
$ npx create-react-app <プロジェクト名> --template typescript
$ cd <プロジェクト名>
$ npm start
```

#### 必要な環境を整える。

[PrettierとESLintを基本からまとめてみた【アプリにESLintとPrettierを導入】](https://qiita.com/kanfutrooper/items/afd44e1a339ff6963ebf)
[React Bootstrap](https://react-bootstrap.github.io/)

```
$ npm install react-bootstrap@next bootstrap@5.1.1
```


## コンポーネント・ファイル構成

```
src
 ├── components
      ├── CreateNotes.tsx
      ├── Header.tsx
      ├── Notes.tsx
      └── NotesList.tsx 
 ├── models
      └── note.model.ts     　
 ├── App.js
```

```piblic/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
//追加コード
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css"
      integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor"
      crossorigin="anonymous"
    />
```

```src/components/CreateNotes.tsx
import * as React from 'react';
import Button from 'react-bootstrap/Button';
import { Alert, Form } from 'react-bootstrap';
import { Note } from '../models/note.model';

interface ICreateNotesProps {
  notes: Note[];
  setNotes: React.Dispatch<React.SetStateAction<Note[]>>;
}

const CreateNotes: React.FunctionComponent<ICreateNotesProps> = ({
  notes,
  setNotes,
}) => {
  const [error, setError] = React.useState<string>('');
  const titleRef = React.useRef<HTMLInputElement | null>(null);
  const textRef = React.useRef<HTMLTextAreaElement | null>(null);
  const colorRef = React.useRef<HTMLInputElement | null>(null);

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
    e.preventDefault();
    if (titleRef.current?.value === '' || textRef.current?.value === '') {
      return setError('ALL friends are mandatory');
    }

    setError('');
    setNotes([
      ...notes,
      {
        id: new Date().toString(),
        title: (titleRef.current as HTMLInputElement).value,
        text: (textRef.current as HTMLTextAreaElement).value,
        color: (colorRef.current as HTMLInputElement).value,
        date: new Date().toString(),
      },
    ]);
    (titleRef.current as HTMLInputElement).value = '';
    (textRef.current as HTMLTextAreaElement).value = '';
  };

  return (
    <>
      <h2>Create Notes</h2>
      {error && <Alert variant='danger'>{error}</Alert>}
      <Form className='mt-3 mb-3' onSubmit={(e) => handleSubmit(e)}>
        <Form.Group className='mb-3' controlId='formBasicTitle'>
          <Form.Label>Title</Form.Label>
          <Form.Control
            type='text'
            placeholder='Enter Title for the Note'
            ref={titleRef}
          />
        </Form.Group>
        <Form.Group className='mb-3' controlId='formBasicText'>
          <Form.Label>Text</Form.Label>
          <Form.Control
            type='text'
            placeholder='Enter your Notes'
            as='textarea'
            rows={3}
            ref={textRef}
          />
        </Form.Group>
        <Form.Group className='mb-3'>
          <Form.Label htmlFor='colorInput'>Notes Color</Form.Label>
          <Form.Control
            type='color'
            id='colorInput'
            defaultValue='#dfdfdf'
            title='Choose your color'
            ref={colorRef}
          />
        </Form.Group>
        <Button type='submit' variant='outline-dark'>
          Submit
        </Button>
      </Form>
    </>
  );
};

export default CreateNotes;
```
```src/components/Header.tsx
import * as React from 'react';
import { Container, Navbar } from 'react-bootstrap';

interface IHeaderProps {}

const Header: React.FunctionComponent<IHeaderProps> = (props) => {
  return (
    <Navbar fixed='top' bg='dark' variant='dark'>
      <Container>
        <Navbar.Brand>React TypeScript Notes</Navbar.Brand>
      </Container>
    </Navbar>
  );
};

export default Header;
```
```src/components/Notes.tsx
import * as React from 'react';
import { Card } from 'react-bootstrap';
import Button from 'react-bootstrap/Button';
import { Note } from '../models/note.model';

interface INotesProps {
  note: Note;
  handleDelete: (id: string) => void;
}

const Notes: React.FC<INotesProps> = ({ note, handleDelete }) => {
  return (
    <div className='mb-3'>
      <Card style={{ backgroundColor: note.color }}>
        <Card.Body>
          <Card.Title>{note.title}</Card.Title>
          <Card.Text>{note.text}</Card.Text>
          <Card.Subtitle className='text-muted'>{note.date}</Card.Subtitle>
          <Button
            className='mt-3'
            variant='outline-dark'
            onClick={() => handleDelete(note.id)}
          >
            Delete
          </Button>
        </Card.Body>
      </Card>
    </div>
  );
};

export default Notes;
```
```src/components/NotesLisr.tsx
mport * as React from 'react';
import Notes from './Notes';
import { Note } from '../models/note.model';

interface INotesListProps {
  notes: Note[];
  setNotes: React.Dispatch<React.SetStateAction<Note[]>>;
}

const NotesList: React.FC<INotesListProps> = ({ notes, setNotes }) => {
  const handleDelete = (id: string) => {
    setNotes(notes.filter((note) => note.id !== id));
  };
  const renderNotes = (): JSX.Element[] => {
    return notes.map((note) => {
      return <Notes key={note.id} note={note} handleDelete={handleDelete} />;
    });
  };
  return (
    <>
      <h2 className='mt-3'>Notes</h2>
      <div>{renderNotes()}</div>
    </>
  );
};
export default NotesList;
```
```src/models/note.model.ts
export interface Note {
  id: string;
  title: string;
  text: string;
  color: string;
  date: string;
}
``` 
```src/App.tsx
import React, { useState } from 'react';
import Header from './components/Header';
import CreateNotes from './components/CreateNotes';
import NotesList from './components/NotesList';
import { Note } from './models/note.model';
import './App.css';
import { Col, Container, Row } from 'react-bootstrap';

function App() {
  const [notes, setNotes] = useState<Note[]>([
    {
      id: new Date().toString(),
      title: 'Meetings',
      text: 'Schedule meeting with UI/UX Team',
      color: '#dfdfdf',
      date: new Date().toString(),
    },
  ]);

  return (
    <>
      <Header />
      <Container className='mt-5'>
        <Row>
          <Col>
            <NotesList notes={notes} setNotes={setNotes} />
          </Col>
        </Row>
      </Container>
      <Container className='mt-5'>
        <Row>
          <Col>
            <CreateNotes notes={notes} setNotes={setNotes} />
          </Col>
        </Row>
      </Container>
    </>
  );
}

export default App;

```


## 参考サイト
[Learn React TypeScript & Bootstrap with one Project | React TypeScript Tutorial for Beginners](https://www.youtube.com/watch?v=HMP5e5utTwg)