15
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Reactでログイン機能を実装する。(Step4: ログインAPIのレスポンスに応じて、画面の出しわけをする。)

Posted at

前回記事の続きです。

ReactのAPIレスポンスの結果に応じて、表示する画面を切り替える。
※本記事ではValidationチェックが通るかのみで、認証のステータスは判断している。

成果物

ログイン画面
スクリーンショット 2022-08-11 22.01.36.png

ログイン成功
スクリーンショット 2022-08-11 22.02.05.png

ログイン失敗
スクリーンショット 2022-08-11 22.02.27.png

環境情報

~/develop/react/react_study/login$ tree -I node_modules     
.
├── README.md
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.tsx
│   ├── components
│   │   ├── Header.tsx
│   │   └── Login.tsx
│   ├── index.tsx
│   ├── pages
│   │   ├── FailedLogin.tsx
│   │   └── Home.tsx
│   ├── router
│   │   └── Router.tsx
│   └── types
│       └── User.ts
├── tsconfig.json
└── yarn.lock
package.json
{
  "name": "login",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@emotion/react": "^11.10.0",
    "@emotion/styled": "^11.10.0",
    "@mui/icons-material": "^5.8.4",
    "@mui/material": "^5.10.0",
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^13.0.0",
    "@testing-library/user-event": "^13.2.1",
    "@types/jest": "^27.0.1",
    "@types/node": "^16.7.13",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "axios": "^0.27.2",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-query": "^4.0.0",
    "react-router-dom": "^6.3.0",
    "react-scripts": "5.0.1",
    "typescript": "^4.4.2",
    "web-vitals": "^2.1.0"
  },
  "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"
    ]
  }
}

Hands-on

src/index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { Router } from "./router/Router";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <Router />
    </BrowserRouter>
  </React.StrictMode>
);
src/App.tsx
import CssBaseline from "@mui/material/CssBaseline";
import { ThemeProvider, createTheme } from "@mui/material/styles";
import Header from "./components/Header";
import { Login } from "./components/Login";

function App() {
  const theme = createTheme({
    palette: {
      mode: "light",
    },
  });

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <Header />
      <Login />
    </ThemeProvider>
  );
}

export default App;
src/components/Header.tsx
import AppBar from "@mui/material/AppBar";
import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";

export default function Header() {
  return (
    <Box sx={{ flexGrow: 1 }}>
      <AppBar position="static">
        <Toolbar>
          <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
            ヘッダー
          </Typography>
        </Toolbar>
      </AppBar>
    </Box>
  );
}
src/components/Login.tsx
import {
  Box,
  Button,
  Card,
  CardActions,
  CardContent,
  CardHeader,
  TextField,
} from "@mui/material";
import axios from "axios";
import { memo, useState } from "react";
import { User } from "../types/User";
import { useNavigate } from "react-router-dom";

export const Login = memo(() => {
  const [userId, setUserId] = useState("");
  const [password, setPassword] = useState("");
  const navigate = useNavigate();

  const cardStyle = {
    display: "block",
    transitionDuration: "0.3s",
    height: "450px",
    width: "400px",
    variant: "outlined",
  };

  const onClickLogin = async () => {
    const authStatus = await axios
      .post<User>("http://localhost:1323/api/login", {
        user_id: userId,
        password,
      })
      .then(() => navigate("/home"))
      .catch((error) => {
        navigate("/fail_login");
      });
    console.log("authStatus: ", authStatus);
  };

  return (
    <Box
      display="flex"
      alignItems="center"
      justifyContent="center"
      padding={20}
    >
      <Card style={cardStyle}>
        <CardHeader title="ログインページ" />
        <CardContent>
          <div>
            <TextField
              fullWidth
              id="username"
              type="email"
              label="Username"
              placeholder="Username"
              margin="normal"
              onChange={(e) => setUserId(e.target.value)}
            />
            <TextField
              fullWidth
              id="password"
              type="password"
              label="Password"
              placeholder="Password"
              margin="normal"
              onChange={(e) => setPassword(e.target.value)}
            />
          </div>
        </CardContent>
        <CardActions>
          <Button
            variant="contained"
            size="large"
            color="secondary"
            onClick={onClickLogin}
          >
            Login
          </Button>
        </CardActions>
      </Card>
    </Box>
  );
});
src/pages/FailedLogin.tsx
import { memo } from "react";

export const FailedLogin = memo(() => {
  return <p>ログイン失敗です。</p>;
});
src/pages/Home.tsx
import { memo } from "react";

export const Home = memo(() => {
  return <p>Homeページです</p>;
});
src/router/Router.tsx
import { Route, Routes } from "react-router-dom";
import Header from "../components/Header";
import { Login } from "../components/Login";
import { FailedLogin } from "../pages/FailedLogin";
import { Home } from "../pages/Home";

export const Router = () => {
  return (
    <Routes>
      <Route
        path="/login"
        element={
          <>
            <Header />
            <Login />
          </>
        }
      />
      <Route path="/home" element={<Home />} />
      <Route path="/fail_login" element={<FailedLogin />} />
    </Routes>
  );
};
src/types/User.ts
export interface User {
  user_id: string;
  password: string;
}

→はい、これで完成!!!

15
6
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
15
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?