你是否花费太多时间进行应用开发了?React+Amplify+AppSync+TypeScript【入门工具包No.3】推荐快速原型制作

这篇文章参考了以下文章并进行了最新版本的更新。
https://qiita.com/G-awa/items/a5b2cc7017b1eceeb002
https://qiita.com/otanu/items/2c522a652e5843a5e2c5

现代前端技术的重要性

本教程旨在介绍现代前端技术概论(我打算以连贯的方式来进行讲解)。
(我之后还打算好好写一篇关于此话题的文章。)

快速原型制作

为了将想法迅速呈现成形,需要具备快速原型设计的技能。为此,需要高效的架构和模板。本教程将介绍模板的设置和部署步骤,请尝试将其作为高速原型设计的基础代码使用。

这里是 GitHub
https://github.com/makotunes/starter-kit-react-gq-amplify

screencapture-localhost-3000-full-todos-2021-06-04-12_26_22.png

我們將使用僅需幾個命令便能將想法迅速實現的應用程式進行部署。

我会安装create-react-app和amplify命令。

npm i -g create-react-app
npm install -g @aws-amplify/cli

地點是中國的一個城市。

$ create-react-app --version
4.0.3

$ node -v
v16.1.0

$ npm -v
7.11.2

$ amplify --version
4.49.0

首先,我们将创建一个框架。

create-react-app speedy-app-starter-kit  --template typescript
cd speedy-app-starter-kit

在AWS中,GraphQL的托管服务称为AppSync。而Amplify可以作为一个CI/CD服务,用于轻松部署托管服务。您可以使用交互式命令来配置设置。根据您的喜好设置AWS凭据等。

amplify init

? Enter a name for the project (speedyappstarterkit)
? Initialize the project with the above configuration? (Y/n) 
? Select the authentication method you want to use: (Use arrow keys)
? Please choose the profile you want to use (Use arrow keys)

在这个阶段,amplify将部署一个空的应用程序。

screencapture-console-aws-amazon-amplify-home-2021-06-03-07_17_16.png

在这里设置GraphQL的API。

amplify add api

? Please select from one of the below mentioned services: (Use arrow keys)
> GraphQL

? Provide API name: 
> speedyappstarterkit

? Choose the default authorization type for the API (Use arrow keys)
> API key

? Enter a description for the API key:
> test

? After how many days from now the API key should expire (1-365):
> 90

? Do you want to configure advanced settings for the GraphQL API 
> No, I am done.

? Do you have an annotated GraphQL schema? No
> No

? Choose a schema template: (Use arrow keys)
> Single object with fields (e.g., “Todo” with ID, name, description) 

? Do you want to edit the schema now?
> No

将创建一个包含数据类型定义的GraphQL模式文件的文件。我们将直接使用这个文件。

type Todo @model {
  id: ID!
  name: String!
  description: String
}

GraphQL注册服务架构时需要提供模式(schema),
但用户只需要提供最基本的信息。

使用amplify的命令行界面,根据这个数据类型定义,自动为AppSync生成模式并为前端生成必要的基本查询模式。

amplify push

? Are you sure you want to continue? (Y/n)
> Y

? Do you want to generate code for your newly created GraphQL API (Y/n)
> Y

? Choose the code generation language target
> typescript

? Enter the file name pattern of graphql queries, mutations and subscriptions (src/graphql/**/*.ts) 
> src/graphql/**/*.ts

? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions (Y/n)
> Y

? Enter maximum statement depth [increase from default if your schema is deeply nested] (2) 
> 2

? Enter the file name for the generated code (src/API.ts)  
> src/API.ts

我会安装最低必要的模块。

yarn add aws-amplify
# yarn add aws-amplify-react
yarn add react-router-dom
yarn add @types/react-router-dom 

为了让工程师也能制作出时尚的设计,我们会引入Material-UI。

yarn add @material-ui/core @material-ui/icons @material-ui/data-grid
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";

import SimpleTodoPage from "./components/pages/SimpleTodoPage";

const App: React.FC = () => {
  return (
    <Router>
      <Switch>
        <Route path="/" component={SimpleTodoPage} exact />
      </Switch>
    </Router>
  );
};

export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Amplify from "aws-amplify"  // 追加
import config from "./aws-exports" // 追加
Amplify.configure(config)          // 追加

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

import GenericTemplate from "../templates/GenericTemplate";
import { makeStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";

import { ThemeProvider } from '@material-ui/styles';
import { createMuiTheme } from '@material-ui/core/styles';
import Box from "@material-ui/core/Box";
import TextField from "@material-ui/core/TextField";
import Button from "@material-ui/core/Button";


import React, { useEffect, useState } from "react";
import { API, graphqlOperation } from "aws-amplify";
import { listTodos } from "../../graphql/queries";
import { createTodo } from "../../graphql/mutations";
import { onCreateTodo } from "../../graphql/subscriptions";
import {
    ListTodosQuery,
    OnCreateTodoSubscription,
    CreateTodoMutationVariables
} from "../../API";

const theme = createMuiTheme({
    palette: {
        primary: { main: '#00838f' },
        secondary: { main: '#e0f7fa' },
    },
});

type Todo = {
    id: string;
    name: string;
    description?: string | null | undefined;
    createdAt: string;
    updatedAt: string;
};

type FormState = {
    name: string;
    description: string;
};

type TodoSubscriptionEvent = { value: { data: OnCreateTodoSubscription } };

const useTodos = () => {
    const [todos, setTodos] = useState<Todo[]>([]);

    useEffect(() => {
        (async () => {
            // 最初のTodo一覧取得
            const result = await API.graphql(graphqlOperation(listTodos));
            if ("data" in result && result.data) {
                const todos = result.data as ListTodosQuery;
                if (todos.listTodos) {
                    setTodos(todos.listTodos.items as Todo[]);
                }
            }

            // Todo追加イベントの購読
            const client = API.graphql(graphqlOperation(onCreateTodo));
            if ("subscribe" in client) {
                client.subscribe({
                    next: ({ value: { data } }: TodoSubscriptionEvent) => {
                        if (data.onCreateTodo) {
                            const todo: Todo = data.onCreateTodo;
                            setTodos(prev => [...prev, todo]);
                        }
                    }
                });
            }
        })();
    }, []);

    return todos;
};


const useStyles = makeStyles({
    table: {
        minWidth: 650,
    },
});

const ProductPage: React.FC = () => {
    const classes = useStyles();
    const [input, setInput] = useState<FormState>({
        name: "",
        description: ""
    });
    const todos = useTodos();

    const onFormChange = ({
        target: { name, value }
    }: React.ChangeEvent<HTMLInputElement>) => {
        setInput(prev => ({ ...prev, [name]: value }));
    };

    const onTodo = () => {
        if (input.name === "" || input.description === "") return;
        const newTodo: CreateTodoMutationVariables = {
            input: {
                name: input.name,
                description: input.description
            }
        };
        setInput({ name: "", description: "" });
        API.graphql(graphqlOperation(createTodo, newTodo));
    };

    return (
        <GenericTemplate title="TODO簡易版">
            <ThemeProvider theme={theme}>
                <Box p={2} bgcolor="primary.main" color="primary.contrastText">
                    新規登録
               </Box>

                <Box p={2} bgcolor="secondary.main" color="primary.main">
                    <form action="/users" acceptCharset="UTF-8" method="post">
                        <div><TextField id="name" type="text" name="name" label="名前" style={{ width: 500 }} value={input.name} onChange={onFormChange} /></div>
                        <div><TextField id="description" type="text" name="description" label="詳細" style={{ width: 500 }} value={input.description} onChange={onFormChange} /></div>
                        <Button variant="contained" color="primary" name="commit" onClick={onTodo}>登録</Button>
                    </form>
                </Box>
            </ThemeProvider>
            <TableContainer component={Paper}>
                <Table className={classes.table} aria-label="simple table">
                    <TableHead>
                        <TableRow>
                            <TableCell>名前</TableCell>
                            <TableCell>詳細</TableCell>

                        </TableRow>
                    </TableHead>
                    <TableBody>
                        {todos.map((row) => (
                            <TableRow key={row.id}>
                                <TableCell component="th" scope="row">
                                    {row.name}
                                </TableCell>
                                <TableCell>{row.description}</TableCell>

                            </TableRow>
                        ))}
                    </TableBody>
                </Table>
            </TableContainer>
        </GenericTemplate>
    );
};

export default ProductPage;

只要没有端口冲突,运行Web服务并确认操作。可以通过http://localhost:3000/访问。

yarn start

这次我们提供了简易版的代码,
完整版也可以在Github上使用。
https://github.com/makotunes/starter-kit-react-gq-amplify

在完整版中,除了浏览添加之外,还支持删除和修改操作。

我也准备了有关前端开发概述的幻灯片。你可以在这里找到:https://speakerdeck.com/makotunes/modanhurontoendoji-shu-gai-lun

bannerAds