1.代理服务器解决跨域问题:
例如:项目启动http://locallhost:3000,而请求的地址http://locallhost:5000
解决方案:
react-cli
的package.json
中添加"proxy": "http://localhost:5000"
设置代理服务器对请求进行转发
2.post请求参数的处理:
axios
默认是使用json格式
的请求体携带参数数据
但是后端对于post
请求大多还是类似于name=tom&pwd=123
这种格式
所以这里需要对数据进行统一;
来看下github
中的axios
如何处理:github.com/axios/axios
- 引入
qs
了,然后调用qs.stringify
对参数对象进行格式化处理
即可
项目中最好使用axios
的请求、响应拦截器
进行处理(Interceptors)
3.axios拦截器的简单使用:
🚀请求拦截器:
post
请求发送前对参数的处理(qs.stringify(data)
)
🚀响应拦截器:
- 对请求
回包的数据
提前进行统一进行错误处理
- 简化相应结果的获取
//axios.js
// 封装能发ajax的请求的函数
import axios from "axios";
import qs from "qs";
import {message} from "antd";
//🚀 请求拦截器 在发送请求数据之前,提前对数据进行处理
axios.interceptors.request.use(function (config) {
console.log("我是拦截器",config);
const {method,data} = config;
if(method === "post" && typeof data === "object"){
//🚀 将通过qs处理过的数据复制给config.data
config.data = qs.stringify(data);
}
return config;
}, function (error) {
return Promise.reject(error);
});
//🚀 响应拦截器
axios.interceptors.response.use(function (response) {
// 返回response.data后,请求的结果就不用再添加.data进行获取了
return response.data;
}, function (error) {
message.error("请求失败了"+ error.message);
// 🚀 让错误出于pending状态,不再往下面进行
return new Promise(()=>{})
// return Promise.reject(error);
});
export default axios;
4.侧面动态菜单栏相关:
- 首先是在React生命周期
componentWillMount
组件加载前,将菜单配置的内容存到this.menuNodes
- 如果有
children
会生成SubMenu
- 如果有
defaultSelectedKeys
:一级路由就是默认选中的菜单项目defaultOpenKeys
:这个是二级路由选中后,刷新浏览器后,二级路由打开的配置- 让
路由地址
与当前菜单的某个子菜单的key
匹配,如果匹配上了,那么让这个匹配上的item的key
赋值给this.openkeys
- 让
import React, {Component} from 'react';
import {Layout, Menu} from "antd";
import {Link,withRouter} from "react-router-dom";
import Logo from "../../assets/images/logo.png";
import "./index.less";
import menuConfig from "../../config/menuConfig";
const { Sider,} = Layout;
const { SubMenu } = Menu;
class LeftNav extends Component {
componentWillMount() {
// 会被加载一次 在render之前执行
console.log("我执行啦")
this.menuNodes = this.getMenuNodes(menuConfig);
console.log(this.menuNodes);
}
//根据指定的菜单数据产生<Menu.Item>的节点
//如果有children会生成SubMenu
//map + 递归
getMenuNodes = (menuList) => {
// 🚀 当前路由地址path
const path = this.props.location.pathname;
return menuList.map((item)=>{
console.log("我是menuitem",item);
if(!item.children){
//生成Menu.item
return(
<Menu.Item key={item.key} icon = {item.icon}>
<Link to={item.key}>
{item.title}
</Link>
</Menu.Item>
)
}else{
//需求:刷新后默认打开二级菜单
//🚀 如果当前那请求路由地址与当前菜单的某个子菜单的key匹配,将父菜单的key保存到openkey
const cItem = item.children.find(cItem => cItem.key===path);
if(cItem){
this.openKeys = item.key;
}
return(
<SubMenu key={item.key} icon = {item.icon} title={item.title}>
{/* 🚀这里面用到了递归操作*/}
{
this.getMenuNodes(item.children)
}
</SubMenu>
)
}
})
}
render() {
// 🚀 一级路由刷新后选中样式丢失的问题
let defaultKeys = this.props.location.pathname;
return (
<Sider trigger={null} collapsible collapsed={this.props.collapsed}>
<div className="logo">
<Link className="left-nav-link" to="/">
<img src={Logo}/>
<h1>海贼王管理平台</h1>
</Link>
<Menu
defaultSelectedKeys={[defaultKeys]}
defaultOpenKeys={[this.openKeys]}
mode="inline"
theme="dark"
>
{
this.menuNodes
}
</Menu>
</div>
</Sider>
);
}
}
export default withRouter(LeftNav);
5.antd4.x中input封装动态赋值的问题:
需求场景:现在想要封装一个input
框,在点击列表的某个项目时,将项目name
带过去,传递给封装的input框,动态赋值并且可以修改; 再一个就是需要在父组件拿到子组件的封装好的input
的值。(更多关于antd3.x迁移到4.x请看ant-design.gitee.io/components/…)
initialValue={this.props.categoryName}
:初始化值。UNSAFE_componentWillReceiveProps
中在每次变化的时候更新input
中的categoryName
的值。
import React, {Component} from 'react';
import {Form,Input} from "antd";
class AddUpdateForm extends Component {
constructor(props) {
super(props);
this.state = {
categoryName:""
}
}
formRef = React.createRef();
// shouldComponentUpdate(nextProps, nextState, nextContext) {
// this.formRef.current.setFieldsValue({
// categoryName: nextProps.categoryName
// })
// return true;
// }
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
console.log("我是分类的nextProps",nextProps);
this.formRef.current.setFieldsValue({
categoryName: nextProps.categoryName
})
}
render() {
return (
<Form initialValues={{remember:true}}
onFinish={this.onFinish}
ref={this.formRef}
>
<Form.Item
name="categoryName"
rules={[
{
required:true,
message:"品类名为必填项目~!"
}
]}
initialValue={this.props.categoryName}
>
<Input type="text" placerholder="请输入分类名称" onChange={this.handleChange}/>
</Form.Item>
</Form>
);
}
}
export default AddUpdateForm;
-
this.formRef.current.formRef.current.getFieldValue("categoryName")
:通过两层ref
,在父组件中获取子组件的值。
// Category.js
import React, {Component} from 'react';
import {Card,Button,Table,message,Modal} from "antd";
import { reqCategorys,reqUpdateCategory,reqAddCategory} from "../../api/index";
import AddUpdateForm from "./add-update-form";
class Category extends Component {
formRef = React.createRef();
handleOk = () => {
// 🚀 form表单验证
console.log("我是表单的名字啊",this.formRef.current.formRef.current.getFieldValue("categoryName"))
console.log("form",this.formRef)
//做修改的时候showStatus = 2;
//做添加的时候showStatus = 1
}
render() {
return (
<Card
extra = {extra}
>
<Modal title={this.state.showStatus=== 1 ? "添加分类":"显示分类"} visible={this.state.showStatus!==0 } onOk={this.handleOk} onCancel={this.handleCancel}>
//🚀 这里也添加了ref
<AddUpdateForm ref={this.formRef} categoryName={this.state.currentName}>{category.name}</AddUpdateForm>
</Modal>
</Card>
);
}
}
export default Category;
6.antd中upload上传
antd
中的上传<Upload/>
组件需要注意下,onChange
函数的三个参数file
,fileList
和event
。
因为上传后file
和fileList
中的图片文件的名字不一致,为了后续开发方便,需要将两个名字设置成一致的。
如下图的handleChange
函数为onChange
事件绑定的方法:
// 🚀 上传图片时触发的回调函数
handleChange = ({file, fileList }) => {
this.setState({ fileList });
console.log("我是当前上传到图片file",file,fileList)
if(file.status === "done"){
//将数组的最后一个file保存到file变量中
file = fileList[fileList.length -1];
//取出响应数据中的图片的url和文件名
const {name,url} = file.response.data;
//保存到上传的file对象
file.name = name;
file.url = url;
}
console.log("fileList",fileList);
}
7.父组件向子组件传递props时,子组件显示props为undefined
当在父组件中通过异步行为获取应用数据,可能会导致传给子组件的数据为空值
。这里给出两种解决方案:
// 这个方法在render方法之前调用,初始化的render不会被触发
componentWillReceiveProps(nextProps, nextContext) {
console.log("我是下一个props、",nextProps);
const imgs = nextProps.imgs;
if(imgs && imgs.length > 0){
const fileList = imgs.map((imgName,i)=>{
return {
uid:-i,
name:imgName,
status:"done",
url:"http://localhost:5000/upload/" + imgName
}
});
this.setState({fileList});
}
}
static getDerivedStateFromProps(nextProps,prevState){
//该方法内禁止访问this
if(nextProps.imgs && nextProps.imgs.length > 0){
//通过对比nextProps和prevState,返回一个用于更新状态的对象
const fileList = nextProps.imgs.map((imgItem,i)=>{
return {
uid:-i,
name:imgItem,
status:"done",
url:"http://localhost:5000/upload/" + imgItem
}
})
// 🚀 这里返回的对象相当于是在其他地方调用了一次this.setState()
return {
fileList
}
}
//不需要更新状态,返回null
return null
}
getDerivedStateFromProps
直接返回一个对象,在其他地方调用了一次this.setState()
未完待续...