Open8
React+TypeScrript+vite+Material UIを始める
はじめにの情報
React: https://ja.reactjs.org/docs/getting-started.html
Vite: https://ja.vitejs.dev/guide/
Material UI: https://mui.com/material-ui/getting-started/overview/
プロジェクトの作成
ViteでReact+Typescriptでプロジェクト作成
$ npm create vite@latest frontend -- --template react-ts
Need to install the following packages:
create-vite@4.1.0
Ok to proceed? (y) y
Scaffolding project in /home/tetsuya/repo/tiny-pmt/frontend...
Done. Now run:
cd frontend
npm install
npm run dev
プロジェクト作成時のメッセージの通りに、実行してみる。
$ cd frontend/
$ npm install
added 83 packages, and audited 84 packages in 12s
8 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
$ npm run dev
> frontend@0.0.0 dev
> vite
VITE v4.1.4 ready in 329 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h to show help
http://localhost:5173/
をブラウザで開くと以下の通り。
Material UIを追加インストール。
- Default installation
- Roboto font
- Icons
の条件でインストール。
npm install @mui/material @emotion/react @emotion/styled @fontsource/roboto @mui/icons-material
frontend/src/index.css
、frontend/src/App.css
は中身は全部削除
App.tsx
を以下のように変更。
frontend/src/App.tsx
import * as React from 'react';
import Button from '@mui/material/Button';
export default function App() {
return (
<div>
<Button variant="contained">Hello World</Button>
</div>
);
}
メニューバーを表示してみる。
App.css
, index.css
を空のファイルにする。
frontend/src/App.tsx
import * as React from 'react';
import ButtonAppBar from './ButtonAppBar';
export default function App() {
return (
<div>
<ButtonAppBar></ButtonAppBar>
</div>
);
}
frontend/src/ButtonAppBar.tsx
import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
import MenuIcon from '@mui/icons-material/Menu';
export default function ButtonAppBar() {
return (
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="menu"
sx={{ mr: 2 }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
TinyPMT
</Typography>
<Button color="inherit">Login</Button>
</Toolbar>
</AppBar>
</Box>
);
}
frontend/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TinyPMT</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Listを表示してみる。
frontend/src/App.tsx
import * as React from 'react';
import ButtonAppBar from './components/ButtonAppBar';
import TicketsComponent from './components/TicketsComponent';
const tickets = [
{title: "最初のチケット"},
{title: "2番目のチケット"},
{title: "3番目のチケット"},
];
export default function App() {
return (
<div>
<ButtonAppBar></ButtonAppBar>
<TicketsComponent tickets={tickets} />
</div>
);
}
frontend/src/components/TicketsComponent.tsx
import React from "react";
import { List } from "@mui/material";
import TicketComponent from "./TicketComponent";
type TicketsComponentProps = {
tickets: {title: string}[];
}
const TicketsComponent = ({tickets} : TicketsComponentProps) => {
return (
<List dense={false}>
{tickets.map((ticket, i) => <TicketComponent ticket={ticket} key={i}/>)}
</List>
);
}
export default TicketsComponent;
frontend/src/components/TicketComponent.tsx
import React from "react";
import { ListItem, ListItemText } from "@mui/material";
type TicketComponentProps = {
ticket: {title: string}
}
const TicketComponent = ({ ticket }: TicketComponentProps) => {
return (
<ListItem>
<ListItemText primary={ticket.title} />
</ListItem>
);
}
export default TicketComponent;
バックエンドからデータ取得
npm install axios
frontend/src/App.tsx
import * as React from 'react';
import ButtonAppBar from './components/ButtonAppBar';
import TicketsComponent from './components/TicketsComponent';
import axios from "axios";
export default function App() {
const [tickets, setTickets] = React.useState(null);
React.useEffect(() => {
axios.get("http://localhost:8080/tickets").then((response) => {
setTickets(response.data.results);
});
}, []);
return (
<div>
<ButtonAppBar></ButtonAppBar>
{tickets != null ? <TicketsComponent tickets={tickets} /> : ""}
</div>
);
}
バックエンドのコントローラのCORSを設定
backend/src/main/java/com/tehorie/tinypmt/presentation/TicketController.java
@RestController
@Tag(name = "Ticket", description = "The ticket API")
@CrossOrigin("http://localhost:5173")
public class TicketController {