React应用入门(二)发送请求并Mock数据
上一篇文章中,写了一个简单的Form表单,但是并没有提到前端向后端发送请求、以及接收到响应之后表单如何处理。本篇文章将完成这一部分
环境准备
在项目根目录下安装axios,qs,mockjs,运行下列命令
npm install axios qs mockjs
查看控制台是否有报错
注意:如果有报错,一定先解决npm install报错问题再进行后续的步骤
发送请求
axios
axios是一个类似于ajax的一个东东。Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中,来自于axios中文说明
在src文件夹下新建api文件夹,在这里处理所有发起对后端API的调用,至于项目结构和项目的搭建,参考本系列的第一章
mkdir api
调用示例基本如下
axios.request({
url: 'method url',
method: 'POST GET DELETE PUT',
header: {'Content-Type': 'application/json'},
data: requestData,
responseType: 'json'
}).then(response => {
retrun response.data
}).catch(error => {
error.message
})
更多写法请参考官方文档,里面说的很详细。
全局实例
所有对后端API的请求统一由一个axios实例来完成,在这个实例中,我们可以配置所有请求的统一前缀。
在api文件夹下创建request.js文件,文件内容目前如下
import axios from "axios";
const service = axios.create({
baseURL: 'http://localhost:8081/badminton-community'
});
export default service;
虽然axios也有请求前的拦截器、和请求后的拦截器。但是我这里并没有添加,因为我们目前的功能暂时只需要一个baseUrl,不做过多引入,以免知识点太多而忽略了本篇文章的主要思路。后续在用到的时候会再次介绍
具体API请求
在api文件夹下新建loginForm.js文件,代表这个文件处理组件LoginForm中的请求
目前我们的场景是
- 填写表单并校验数据
- 点击登录按钮,开始发起请求调用
这里我们需要做的就是发起请求,并接收响应数据,文件内容如下
import service from "./request";
import responseData from '../mocks/loginFormData'
const qs = require('qs')
export const login = (loginFormData) => {
console.log('api---------------->')
return service
.request({
url: '/login',
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
data: qs.stringify(loginFormData),
responseType: 'json'
}).then(response => {
console.log(response.data)
// 返回数据
return response.data
}).catch(error => {
error.message
})
}
先引入axios全局实例
然后定义了一个login的函数,参数名字为loginFormData。对于后端的同学,如果看不懂箭头函数,强烈推荐这个前端语法全解析的文档,内容清晰明了,示例简单易懂。
-
这里我们使用qs来对数据进行序列化
-
注意我这里的写法,我是在response中return了数据并且又在axios的地方return了。为什么要这样写呢?
因为axios返回的是一个Promise,是异步的,所以在异步的response中我们需要把接收到的数据返回,以便后续的处理中能够拿到响应的数据。
关于如何在axios中返回数据请看stackoverflow上有关此问题的回答,看完这个之后,相信你应该会明白
具体组件中调用发起API调用的函数
已经有了发起API调用的函数,那么我们接下来要处理的就是将具体组件上和API调用的逻辑组合在一起,完成页面的联动
回到LoginForm组件中,在onSubmit中添加响应的处理
import { LoadingButton } from "@mui/lab";
import {IconButton, InputAdornment, Stack, TextField } from "@mui/material";
import { Icon } from '@iconify/react';
import eyeFill from '@iconify/icons-eva/eye-fill';
import eyeOffFill from '@iconify/icons-eva/eye-off-fill';
import { useFormik, Form, FormikProvider } from "formik";
import * as Yup from 'yup';
import React from "react";
import { useState } from "react";
import { login } from "../../../api/loginForm";
export default function LoginForm () {
const [showPassword, setShowPassword] = useState(false);
const handleShowPassword = () => {
setShowPassword((show) => !show);
};
// 定义验证规则
const LoginSchema = Yup.object().shape({
email: Yup.string().email('Email must be a valid email address').required('Email is required'),
password: Yup.string().required('Password is required')
});
const formik = useFormik({
initialValues: {
email: '',
password: '',
remember: true
},
validationSchema: LoginSchema,
onSubmit: (values, props) => {
console.log('request-------->')
login(values).then(data => {
console.log(data)
// 成功之后将setSubmitting设置为false
props.setSubmitting(false)
})
console.log('response------->')
}
});
const { errors, touched, values, isSubmitting, handleSubmit, getFieldProps } = formik;
return (
<FormikProvider value={formik}>
<Form autoComplete="off" noValidate onSubmit={handleSubmit}>
<Stack spacing={3}>
<TextField
autoComplete="username"
type="email"
label="Email Address"
{...getFieldProps('email')}
error={Boolean(touched.email && errors.email)}
helperText={touched.email && errors.email}/>
<TextField
autoComplete="current-password"
type={showPassword ? 'text' : 'password'}
label="Password"
{...getFieldProps('password')}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={handleShowPassword} edge="end">
<Icon icon={showPassword ? eyeFill : eyeOffFill}></Icon>
</IconButton>
</InputAdornment>
)
}}
error={Boolean(touched.password && errors.password)}
helperText={touched.password && errors.password}/>
</Stack>
<LoadingButton
fullWidth
size="large"
type="submit"
variant="contained"
// 当为false时停止加载动画
loading={isSubmitting}
>
Login
</LoadingButton>
</Form>
</FormikProvider>
);
}
页面的逻辑很简单
- 在onSubmit中调用api/loginForm.js的login函数发起请求,同时表单开启loading状态
- 当请求成功时,设置submitting为false,停止表单加载
这里如果对表单的代码不熟悉,可以看React表单组件的使用这篇文章
Mock数据
到此为止,表单的发送请求,以及接受请求之后的页面逻辑都已经处理完毕。我们需要验证一下,是否能够发起对后端的调用并且正常响应。
- 为了减少不必要的前端开发工作,我们没必要去真的开发一个后端应用
- 前后端分离的项目协作模式中,不一定要等到后端的接口出来之后再去联调
所以我们使用前端自己的mock数据。
关于各种mock工具,这里不做过多赘述,有兴趣的可以了解下。因为目前项目结构还很小,所以我们使用最简单的、并且不是太复杂的mock方法
mockjs
最终使用mockjs。mockjs我目前使用的不太爽的地方就是,官方文档给出的示例太少,而且没有一个流程很完成的示例。对于我这种后端开发来说太难受了
在src文件夹下新建mocks文件夹,所有的mock数据都在这里
mkdir mocks
在mocks文件夹下创建loginFormData.js文件,代表是api/loginForm的请求的mock数据,文件内容如下
const Mock = require('mockjs');
export default Mock.mock('http://localhost:8081/badminton-community/login', 'post', (option) => {
// 这种情况需要重写mock
return Mock.mock({
code: 200,
msg: 'success'
})
});
注意:mockjs最坑的一点是拦截的方法类型是要小写的,而且我在官方文档上也没找到说明,这也太坑了吧
然后在api/loginForm.js中引入该文件即可
验证
所有工作都已经完成,目前我们验证手段是通过浏览器控制台打印,以及loading动画是否按照预期关闭这两种方式来验证这一流程是否符合我们的预期
npm run start
在表单中填写数据,点击LOGIN按钮,观察控制台输出
能够看到,流程符合预期。
后续
表单提交这一流程已经基本走通,代表着后续所有API请求,以及mock数据的拦截类似模式都可以打通了。
表单提交成功了之后,后续的逻辑应该是进入Dashboard,并路由,所有下一篇文章将进行路由相关工作的开发