需求描述

在一些场景下,需要对某些字段进行编辑。

例如在一个表格中,需要对一个单元格里的数据进行展示和编辑。

有很多种编辑方案:

  1. 只显示编辑组件,例如这个字段是 string 类型,就直接展示为一个 Input 组件,修改这个组件内容会触发字段更新。
  2. 在展示组件与编辑组件之间切换。例如这个字段是 string 类型,展示为一个 string 组件,点击后展示为 Input 组件。
  3. 正常显示展示组件,弹窗展示编辑组件。

在上述方案中,只有方案 3 可以做到展示组件与编辑组件分离,并且弹窗编辑也能支持更多复杂的编辑手段,所以我们选中方案 3 来做。

设计

基本逻辑为:

  1. 实现一个展示组件。
  2. 实现一个编辑组件。包括了点击按钮和编辑弹窗。
  3. 编辑成功后(与后端交互成功),关闭弹窗,并更新展示组件。

    实现

    ```tsx // display-edit.tsx import { Space } from ‘antd’ import React, { Dispatch, ReactElement, useState } from ‘react’

export function DisplayEdit(props: { defaultEditData: T displayNodeRender: (data: T) => ReactElement editNodeRender: (data: T, setData: Dispatch) => ReactElement }): ReactElement { const { defaultEditData, displayNodeRender, editNodeRender } = props

const [editData, setEditData] = useState(defaultEditData)

return ( {displayNodeRender(editData)} {editNodeRender(editData, setEditData)} ) }

  1. ```tsx
  2. // page.tsx
  3. import { EditTwoTone } from '@ant-design/icons'
  4. import { Button, Form, Input, Modal, Space } from 'antd'
  5. import React, { FC, useState } from 'react'
  6. import { DisplayEdit } from './display-edit'
  7. type EditData = {
  8. id: string
  9. name: string
  10. like: string
  11. }
  12. const Display: FC<EditData> = (props) => {
  13. const { name, like } = props
  14. return (
  15. <Space direction="vertical">
  16. <span>Name: {name}</span>
  17. <span>Like: {like}</span>
  18. </Space>
  19. )
  20. }
  21. const Edit: FC<{
  22. initialValues: EditData
  23. onSuccess: (newData: EditData) => void
  24. }> = (props) => {
  25. const { initialValues, onSuccess } = props
  26. const [visible, setVisible] = useState(false)
  27. return (
  28. <>
  29. <div onClick={() => setVisible(true)}>
  30. <EditTwoTone />
  31. </div>
  32. <Modal
  33. visible={visible}
  34. onCancel={() => setVisible(false)}
  35. title="Edit"
  36. footer={null}
  37. >
  38. <Form<EditData>
  39. layout="vertical"
  40. initialValues={initialValues}
  41. onFinish={(formValues) => {
  42. onSuccess(formValues)
  43. setVisible(false)
  44. }}
  45. >
  46. <Form.Item label="ID" name="id">
  47. <Input />
  48. </Form.Item>
  49. <Form.Item label="Name" name="name">
  50. <Input />
  51. </Form.Item>
  52. <Form.Item label="Like" name="like">
  53. <Input />
  54. </Form.Item>
  55. <Form.Item>
  56. <Button htmlType="submit" type="primary">
  57. Submit
  58. </Button>
  59. </Form.Item>
  60. </Form>
  61. </Modal>
  62. </>
  63. )
  64. }
  65. export const Page: FC = () => {
  66. return (
  67. <DisplayEdit<EditData>
  68. defaultEditData={{ id: '1', name: 'fy', like: 'code' }}
  69. displayNodeRender={(editData) => <Display {...editData} />}
  70. editNodeRender={(editData, setEditData) => (
  71. <Edit
  72. initialValues={editData}
  73. onSuccess={(newData) => setEditData(newData)}
  74. />
  75. )}
  76. />
  77. )
  78. }

示意图:
Kapture 2022-04-18 at 15.22.37.gif
[END]