关于antd中可编辑单元格的另一种思路

一些题外话

我最近有快2个月没有更新博客了,不是我已经忘记了这件事,一个原因是最近一直没有遇到什么值得记录的问题以及知识点。如果要硬写也不是写不出来,毕竟自己每天也有看别人的博客或者知识点。只是我认为这些东西就算写出来80%也是搬运别人的,没有太大的意义。另外一个原因是我的小闺女刚出生,事情有点多。

进入正题

因为新的需求中需要用到可编辑单元格的表格,所以就阅读的antd的文档看有没有合理的实现方案。
对于antd中的Table组件大家应该都不陌生,功能非常齐全且强大,使用起来也比较简单。其中就有一个例子是对于自定义单元格的使用的:
在这里插入图片描述
虽然当时看到有现成的功能很开心,但是读了对应的代码之后就觉得这个实现好复杂,需要完全自定义rowComponentcellComponent,同时还需要用到React中的上下文Context,这个使用起来更麻烦,而且也会使得数据流变的奇怪。所以我希望有一种更加简单并且容易理解的方式,我想到一个方案:是否可以对column配置项中的render函数进行自定义,返回一个可以编辑的div就可以,然后再实现一些与父组件的交互就可以了

经过实践过后完全可行,下面上代码:

TableTest.tsx

import { Table } from 'antd';
import React, { useEffect, useState } from 'react';
import EditDiv from './EditDiv';

const TableTest = () => {
  const [data, setData] = useState<Record<keyof any,any>[]>([]);

  useEffect(() => {
    const updateData = [
      {
        key: '0',
        name: 'Edward King 0',
        age: '32',
        address: 'London, Park Lane no. 0',
        operation: 'London, Park Lane no. 0',
      },
      {
        key: '1',
        name: 'Edward King 1',
        age: '32',
        address: 'London, Park Lane no. 1',
        operation: 'London, Park Lane no. 1',
      },
    ]
    setData(updateData);
  }, [])

  const updateData = (val) => {
  	// 此处模拟异步请求,需要将对应的promise对象返回,在EditDiv文件中会进行处理
    const promise = new Promise<boolean>((resolve) => {
      setTimeout(() => {
        resolve(true)
      }, 1000)
    });
    return promise;
  }

  const columns = [
    {
      title: 'name',
      dataIndex: 'name',
      width: '30%',
      editable: true,
    },
    {
      title: 'age',
      dataIndex: 'age',
    },
    {
      title: 'address',
      dataIndex: 'address',
    },
    {
      title: 'operation',
      dataIndex: 'operation',
      render: (text) => {
        return <EditDiv value={text} onValueChange={updateData} />
      }
    },
  ];

  return (
    <Table
      dataSource={data}
      columns={columns}
    />
  )
}

export default TableTest;

这个文件中的columns的render最终返回的是一个EditDiv组件:一个可以编辑的div。

EditDiv.tsx

import React, { useEffect, useReducer, useRef, useState } from 'react';

interface IEditDivProps{
  value: string | number;
  onValueChange?: (val: string) => Promise<boolean>;
}

const reducer = (state, { type, payload }) => {
  switch(type) {
    case 'update':
      return {
        ...state,
        ...payload
      }
    case 'reset': 
      return {
        ...state,
        isEdit: false
      }
    default: 
      return {
        ...state
      }
  }
}

const EditDiv = ({ value, onValueChange }:IEditDivProps) => {
  const initState = {
    isEdit: false,
    val: value,
    loading: false
  }
  // 此处使用了useReducer,这个只是自己想用,其实也可以分别拆开使用
  const [state, setState] = useReducer(reducer, initState);
  const inputRef = useRef<HTMLInputElement>(null);

  // 当进入编辑状态input自动获取焦点
  useEffect(() => {
    if(state.isEdit && inputRef.current) {
      inputRef.current.focus();
    }
  }, [state.isEdit])

  const handleVal = (e) => {
    setState({type: 'update', payload: { val: e.target.value }});
  }

  const showInput = () => {
    setState({type: 'update', payload: { isEdit: true }});
  }

  // 处理valueChange中返回的promise对象,如果返回true则代表服务已经修改成功,需要前端相应修改
  // 如果为false则需要将input还原为原来的值
  const hideInput = async () => {
    if(onValueChange) {
      setState({type: 'update', payload: { loading: true }});
      const res = await onValueChange(state.val);
      if(res) {
        setState({type: 'update', payload: { loading: false, isEdit: false }});
      } else {
        setState({type: 'update', payload: { loading: false, val: value, isEdit: false }});
      }
    }else {
      setState({type: 'update', payload: { isEdit: false }});
    }
  }

  // 监听回车事件
  const handleKey = (e) => {
    if(e.keyCode === 13) {
      hideInput();
    }
  }

  const renderElement = () => {
    const { val, isEdit, loading } = state;
    if(isEdit) {
      if(loading) {
        return 'loading...'
      } else {
        return (
          <input
            ref={inputRef}
            value={val}
            onChange={handleVal}
            onBlur={hideInput}
            onKeyUp={handleKey}
          />
        )
      }
    }else {
      return (
        <div onClick={showInput} style={{height: "1em", cursor: 'pointer'}}>{val}</div>
      )
    }
  }

  return (
    <div>
      {renderElement()}
    </div>
  )
}

export default EditDiv;

结语

好了,以上就是我根据自己的思路实现的自定义表格,我本地试了之后是完全可行的,就是还没有添加css样式,所以看起来有点丑,但是影响不是很大。希望对阅读的各位有所帮助,如果帮到了你,希望能点赞支持一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值