前言
最近开发项目,碰到了一个父子组件传输数据的需求,也碰上了两个有点‘棘手’的问题,这里记录一下我的解决思路。先展示一下基本的代码和页面效果:
父组件:
import TableList from 'src/components/TableList'
import { Button } from 'antd'
const List: React.FC = () => {
return (
<div style={{marginTop: '20px'}}>
<TableList />
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: '40px',
backgroundColor:'white'
}}
>
<div>
<Button type="text">X selected</Button>
<Button type="link">Select All</Button>
<Button type="link">Select Invert</Button>
<Button type="link">Deselect All</Button>
</div>
<div>
<Button type="primary">Export</Button>
</div>
</div>
</div>
)
}
export default List
子组件:
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import type { TableRowSelection } from 'antd/es/table/interface';
import React, { useState } from 'react';
interface DataType {
key: React.Key;
name: string;
age: number;
address: string;
}
const columns: ColumnsType<DataType> = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Age',
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data: DataType[] = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
const TableList: React.FC = () => {
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
console.log('selectedRowKeys changed: ', newSelectedRowKeys);
setSelectedRowKeys(newSelectedRowKeys);
};
const rowSelection: TableRowSelection<DataType> = {
selectedRowKeys,
onChange: onSelectChange,
selections: [
Table.SELECTION_ALL,
Table.SELECTION_INVERT,
Table.SELECTION_NONE,
],
};
return <Table rowSelection={rowSelection} columns={columns} dataSource={data} />;
};
export default TableList;
子 ---> 父
要想实现子传父,首先我们需要在父组件中定义一个函数来接收数据
const exportData = (value) => {
console.log(value)
}
然后,将此函数写在子组件作为一个属性,
<TableList exportData={exportData} />
下一步,我们来让子组件接收此函数,首先我们需要定义类型(因为写的是typescript)
interface IProps {
exportData: (value) => void
}
还要改造一下子组件
const TableList: React.FC<IProps> = (props: IProps) => {
const { exportData } = props || {}
......
};
export default TableList;
然后我们就可以在 onSelectChange
里面传数据了
const onSelectChange = (newSelectedRowKeys: React.Key[], selectedRows:DataType[]) => {
console.log('selectedRowKeys changed: ', newSelectedRowKeys);
setSelectedRowKeys(newSelectedRowKeys);
exportData(selectedRows)
};
至此我们已经实现了 子 ---> 父
通信
(ps:这里我们需要注意一下,checkbox 选择与否是通过 selectedRowKeys
来控制的,不是通过 selectedRows
)
父 ---> 子
实现父传子很简单,上面其实我们已经介绍过了,通过 props
传给子组件
问题
要想实现点击父组件的按钮,触发子组件的处理逻辑,我原来的想法是父组件点击不同的按钮,传一个 selectType 值给子组件,子组件收到之后,根据不同的值去做数据更新,
// 子组件
React.useEffect(() => {
switch (selectType) {
case 'Select All':
...
break
case 'Deselect All':
...
break
case 'Select Invert':
...
break
}
}, [selectType])
后来自己自测的实时候发现了问题,如果上一次点击了 invert,这次再点击 invert ,按理说应该会做两次数据调整,而页面却仅做了一次调整,问题就在于useEffect的参数,selectType的值没有改变,就不会去匹配,也就不会做数据处理了。
昨天想了一会了,得到了解决思路,如果子组件不能按预期更新,我可以在父组件把数据处理了之后再传给子组件,不就解决了子组件数据问题的了。(gif画质有点糊...)
完整代码为:
子组件
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import type { TableRowSelection } from 'antd/es/table/interface';
import React, { useState } from 'react';
interface DataType {
key: React.Key;
name: string;
age: number;
address: string;
}
interface IProps {
exportData: (keys: React.Key[], values: DataType[], allValues: DataType[]) => void
lists: {
keys: React.Key[]
values: DataType[]
allValues: DataType[]
}
}
const columns: ColumnsType<DataType> = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Age',
dataIndex: 'age',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const data: DataType[] = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
const TableList: React.FC<IProps> = (props: IProps) => {
const { exportData, lists } = props || {}
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
React.useEffect(() => {
onSelectChange(lists.keys, lists.values)
},[lists])
const onSelectChange = (newSelectedRowKeys: React.Key[], selectedRows:DataType[]) => {
setSelectedRowKeys(newSelectedRowKeys);
exportData(newSelectedRowKeys,selectedRows, data)
};
const rowSelection: TableRowSelection<DataType> = {
selectedRowKeys,
onChange: onSelectChange,
selections: [
Table.SELECTION_ALL,
Table.SELECTION_INVERT,
Table.SELECTION_NONE,
],
};
return <Table rowSelection={rowSelection} columns={columns} dataSource={data} />;
};
export default TableList;
父组件
import * as React from 'react'
import TableList from 'src/components/TableList'
import { Button } from 'antd'
interface DataType {
key: React.Key
name: string
age: number
address: string
}
const List: React.FC = () => {
const [selectedRowKeys, setSelectedRowKeys] = React.useState<React.Key[]>([])
const [data, setData] = React.useState<DataType[]>([])
const [selectedRows, setSelectedRows] = React.useState<DataType[]>([])
const [lists, setLists] = React.useState<{
keys: React.Key[]
values: DataType[]
allValues: DataType[]
}>({
keys: [],
values: [],
allValues: [],
})
const exportData = (
keys: React.Key[],
values: DataType[],
allValues: DataType[]
) => {
setSelectedRowKeys(keys)
setSelectedRows(values)
setData(allValues)
}
return (
<div style={{ marginTop: '20px' }}>
<TableList exportData={exportData} lists={lists} />
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: '40px',
backgroundColor: 'white',
}}
>
<div>
<Button type="text">{selectedRows.length} selected</Button>
<Button
type="link"
onClick={() => {
setLists({
keys: data?.map((item) => item.key),
values: data,
allValues: data,
})
}}
>
Select All
</Button>
<Button type="link" onClick={() => {
setLists({
keys: data?.filter((item) => !selectedRowKeys.includes(item.key)).map((item) => item.key),
values: data?.filter((item) => !selectedRowKeys.includes(item.key)),
allValues: data,
})
}}>Select Invert</Button>
<Button type="link" onClick={() => {
setLists({
keys: [],
values: [],
allValues: data,
})
}}>Deselect All</Button>
</div>
<div>
<Button type="primary">Export</Button>
</div>
</div>
</div>
)
}
export default List
css布局问题
平常我们写需求的时候,可能会遇到这样的需求,左边有一些按钮,右边有一些按钮,之前老是应该怎么实现这些布局?最开始的想法是用 float 或者 position ,可是觉得这样不太好,样式维护起来可能会比较头疼。昨天在网上看 flex 布局的时候,justifyContent: 'space-between'
解决了我的疑问
mac快捷键
功能 | 快捷键 |
---|---|
刷新 | command + R |
定位到地址栏 | command + L |
打开新的标签页 | command + T |
打开新窗口 | command + N |
打开历史记录 | command + Y |
结语
如果有想法或者疑问,欢迎评论区留言