ant design v4笔记
前言
最近在使用ant design pro的时候,遇到很多问题,也花费了不少时间,为了防止后续遗忘,特意记录一下,也算是帮大家踩坑了。
使用哪个版本
目前最新的是ant design pro v4的版本,但是网上大部分的教程还是v3甚至还要早的,v4和v3相比还是很多地方不一样的,大部分实例已经变成react hook的形式,也就是函数组件的形式,在v3的时候大多数还是使用的class。
所以如果是新手学习起步的,建议直接使用v4,学最好学最新的东西,如果是在修改老的项目,优先选用当前的版本的代码,因为改起来还是挺麻烦的,亲身经历。
子组件修改父组件的state
目前有两种情况:
1.使用class的有状态组件进行传值(class那种)
2.使用react hook的函数组件进行传值(function的那种)
下面一一展开
首先是 class 形式函数组件,
import React from "react";
import {Input} from "antd";
export default class Parent extends React.Component{
constructor(props) {
super(props);
this.state ={value:""}
}
handleChange = (newValue) =>{
console.log("父组件获取新值",newValue);
this.setState({
value:newValue
});
}
render() {
return <MyInput value = {this.state.value} onChange = {this.handleChange}/>;
}
}
class MyInput extends React.Component{
handleChange =(event)=>{
// 关键部分这里的this.props会调用父组件的方法,进而修改父类的state
this.props.onChange(event.target.value)
}
render() {
return <Input value={this.props.value} onChange={this.handleChange}/>
}
}
这个例子是在父组件中引入一个包含Input的子组件,子组件的Input组件值发生改变的时候父组件的也受影响发生改变。
因为setState是异步的所以在使用的时候,尤其涉及到count计算之类的时候,最好使用下面的形式
this.setState((preState)=>{
return({count:preState.count+1})
});
// preState是历史的状态
同样的功能使用函数组件的形式实现
import {useState} from "react"
import {Input} from "antd";
export default function Parent() {
// useState是React Hook中的 它的参数含义如下
// 第一个值;value 相当于class里面的this.state={value:""}
// 第二个值:一般约定是setValue这样的形式,它的作用相当于
// this.setState({value:"新的值"})
const [value,setValue] = useState()
function handleChange(newValue){
console.log("父组件获取新值",newValue);
setValue(newValue)
}
return <MyInput value={value} onChange ={handleChange} />
}
function MyInput(props){
function handleChange(event) {
props.onChange(event.target.value)
}
return <Input value={props.value} onChange={handleChange}/>
}
坑人的Form
从v3升级到v4最坑的就是Form,因为我的项目使用的class 的形式所以,这里没有使用React Hook的形式去写,但是官网上v4使用的基本上都是函数组件的形式,在v3中使用Form的时候,需要使用Form.Create包裹下组件才能使用一些值,但是在V4中是不能使用Form.Create的,所以在这期间我调查了好久,最后算是解决了,我用的例子就是官网那个可编辑表格,参考v3和官网的迁移指南最后修改完成
/*eslint-disable */
import {Form, Input, InputNumber, Popconfirm, Table} from 'antd';
import React from "react";
import {FormInstance} from "antd/es/form";
const originData: any = [];
for (let i = 0; i < 100; i+=1) {
originData.push({
key: i.toString(),
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
class EditableCell extends React.Component<any, any> {
getInput = () => {
if (this.props.inputType === 'number') {
return <InputNumber/>;
}
return <Input/>;
};
render() {
const {
editing,
dataIndex,
title,
inputType,
record,
index,
children,
...restProps
} = this.props
return (
<td {...restProps}>
{editing ? (
// 这个地方不用再使用getFieldDecorator(这破玩意至今拼不对)
// 这个name写了之后 初始值就要在Form上设置了 initialValues
<Form.Item name={dataIndex} label={dataIndex} style={{margin: 0}} rules={[{
required: true,
message: `Please Input ${title}!`,
}]}>
{this.getInput()}
</Form.Item>
) : (
children
)}
</td>
);
}
}
class EditableTable extends React.Component<any, any> {
formRef = React.createRef<FormInstance>();
// React Hook使用的 useForm,这里使用React.createRef代替
isEditing = record => record.key === this.props.state.editingKey;
edit(record: any) {
// 这里需要通过setFieldsValue
this.formRef?.current?.setFieldsValue({
name: '',
age: '',
address: '',
...record,
});
// 调用父组件的方法开始传值
this.props.handleEdit(record)
};
cancel = () => {
this.props.handleCancel()
};
save(key: any) {
// getFieldsValue获取当前所选的行的值
const row = this.formRef?.current?.getFieldsValue();
const newData = [...this.props.state.data];
// 根据索引匹配当前的数据
const index = newData.findIndex((item) => key === item.key);
if (index > -1) {
const item = newData[index];
newData.splice(index, 1, {...item, ...row});
} else {
newData.push(row);
}
this.props.handleSave(newData)
};
render() {
const components = {
//修改table该属性默认行为
body: {
cell: EditableCell,
},
};
const columns = [
{
title: 'name',
dataIndex: 'name',
width: '25%',
editable: true,
},
{
title: 'age',
dataIndex: 'age',
width: '15%',
editable: true,
},
{
title: 'address',
dataIndex: 'address',
width: '40%',
editable: true,
},
{
title: 'operation',
dataIndex: 'operation',
render: (text, record) => {
const {editingKey} = this.props.state;
const editable = this.isEditing(record);
return editable ? (
<span>
<a
onClick={() => this.save(record.key)}
style={{
marginRight: 8,
}}
>
Save
</a>
<Popconfirm title="Sure to cancel?" onConfirm={() => this.cancel()}>
<a>Cancel</a>
</Popconfirm>
</span>
) : (
<a aria-disabled={editingKey !== ''} onClick={() => this.edit(record)}>
Edit
</a>
);
},
},
];
const mergedColumns = columns?.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
inputType: col.dataIndex === 'age' ? 'number' : 'text',
dataIndex: col.dataIndex,
title: col.title,
editing: this.isEditing(record),
}),
};
});
return (
// ref属性是必须加的,这样子组件才能使用form的属性
<Form ref={this.formRef} component={false}>
<Table
components={components}
bordered
dataSource={this.props.state.data}
columns={mergedColumns}
rowClassName="editable-row"
pagination={{
onChange: this.cancel,
}}
/>
</Form>
);
}
}
export default class Demo extends React.Component<any, any> {
constructor(props: any) {
super(props);
this.state = {
data: originData,
editingKey: '',
}
}
handleEdit = (record: any) => {
this.setState({editingKey: record.key});
}
handleCancel = () => {
this.setState({editingKey: ''});
}
handleSave = (newData) => {
this.setState({
data: newData,
editingKey: ''
})
}
render() {
const parentMethods = {
handleEdit: this.handleEdit,
handleCancel: this.handleCancel,
handleSave: this.handleSave,
state:this.state,
};
return (
<EditableTable {...parentMethods} />
)
}
}
结语
大概就是这样,关键点都加上注释,我ts写的也不好,这里主要是参考实现方式吧。
大概就这样吧,じゃあ。