環境の準備
ターミナルでreactアプリケーションを作成する。
$ npx create-react-app <プロジェクト名> --template typescript
$ cd <プロジェクト名>
$ npm start
必要な環境を整える。
PrettierとESLintを基本からまとめてみた【アプリにESLintとPrettierを導入】
React Bootstrap
$ 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