You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决Fluent UI React <DetailList/>编辑值后UI不更新的问题

问题根源

  1. useEffect依赖错误:你把editableItemId作为useEffect的依赖,导致每次双击切换编辑状态时,都会重新调用fetchData拉取原始数据,直接覆盖用户修改后的本地数据。
  2. Columns存储方式不当:将columns存在state中且仅在editableItemId变化时更新,无法响应items的变化,可能导致渲染逻辑捕获旧状态。
  3. DetailsList的key设置不合理:使用items.length作为key,当列表长度不变时,即使内容更新,React也不会触发组件重新渲染。

修复步骤

1. 修正useEffect依赖,仅初始化一次数据

把useEffect的依赖改为空数组,确保只在组件首次加载时拉取数据,避免覆盖用户修改:

useEffect(() => {
  const fetchData = async() => {
    const data = await fetchExampleData();
    setItems(data);
  };

  fetchData();
}, []); // 空数组表示仅组件挂载时执行

2. 优化Columns定义,用useMemo+useCallback管理

移除columns的state,改用useMemo结合useCallback确保渲染函数捕获最新状态:

// 用useCallback包裹渲染函数,依赖变化时自动更新
const renderEditableColumn = useCallback((item: ExampleDataItem) => (
  editableItemId === item.id ? (
    <input 
      type="text"
      value={item.editedTitle ?? item.title}
      onChange={(e) => handlerInputChange(e, item.id)}
      onKeyDown={(e) => handleInputKeyDown(e, item.id)}
    />
  ) : (
    <span onDoubleClick={() => handleDoubleClick(item)}>{item.title}</span>
  )
), [editableItemId]);

// 假设renderCenteredColumn是你的其他渲染函数,同样用useCallback包裹
const renderCenteredColumn = useCallback((item: ExampleDataItem) => (
  <div style={{ textAlign: 'center' }}>{item.userId}</div>
), []);

// 用useMemo定义列配置,依赖渲染函数
const columns = useMemo(() => [
  {
    key: 'id',
    name: 'User ID',
    fieldName: 'id',
    minWidth: 80,
    maxWidth: 120,
    isResizable: true
  },
  {
    key: 'title',
    name: 'Case Title',
    fieldName: 'title',
    minWidth: 150,
    maxWidth: 250,
    isResizable: true,
    onRender: renderEditableColumn
  },
  {
    key: 'userId',
    name: 'Case Number',
    fieldName: 'userId',
    minWidth: 80,
    maxWidth: 120,
    isResizable: true,
    onRender: renderCenteredColumn
  }
], [renderEditableColumn, renderCenteredColumn]);

3. 移除DetailsList上不合理的key

删除DetailsList组件的key={items.length}属性,让React根据props变化自动管理渲染:

<DetailsList
  items={items}
  columns={columns}
  setKey="set"
  ariaLabelForSelectionColumn="Toggle selection"
  ariaLabelForSelectAllCheckbox="Toggle selection for all items"
  checkButtonAriaLabel="select row"
/>

完整修复代码

const MyTable: React.FC = () => {
  const [items, setItems] = useState<ExampleDataItem[]>([]);
  const [editableItemId, setEditableItemId] = useState<number | null>(null);

  // 双击切换编辑状态
  const handleDoubleClick = (item: ExampleDataItem) => {
    console.log('handleInputChange called, double click', item);
    setEditableItemId((prevId) => (prevId === item.id ? null : item.id));
  };

  // 可编辑列渲染函数
  const renderEditableColumn = useCallback((item: ExampleDataItem) => (
    editableItemId === item.id ? (
      <input 
        type="text"
        value={item.editedTitle ?? item.title}
        onChange={(e) => handlerInputChange(e, item.id)}
        onKeyDown={(e) => handleInputKeyDown(e, item.id)}
      />
    ) : (
      <span onDoubleClick={() => handleDoubleClick(item)}>{item.title}</span>
    )
  ), [editableItemId]);

  // 输入框内容变化处理
  const handlerInputChange = (e: React.ChangeEvent<HTMLInputElement>, itemId: number) => {
    console.log('handleInputChange called, value changing', e.target.value);
    const updateItems = items.map((item) =>
      item.id === itemId ? { ...item, editedTitle: e.target.value } : item
    );
    setItems(updateItems);
  }

  // 回车键确认编辑
  const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, itemId: number) => {
    console.log('handleInputKeyDown called, new gg');
    if (e.key === 'Enter') {
      setItems((prevItems) => {
        const updatedItems = prevItems.map((item) =>
          item.id === itemId ?
          { ...item, title: item.editedTitle ?? item.title, editedTitle: undefined } :
          item
        );
        console.log('Updated Items:', updatedItems);
        return updatedItems;
      });
      setEditableItemId(null);
    }
  };

  // 居中列渲染函数
  const renderCenteredColumn = useCallback((item: ExampleDataItem) => (
    <div style={{ textAlign: 'center' }}>{item.userId}</div>
  ), []);

  // 列配置
  const columns = useMemo(() => [
    {
      key: 'id',
      name: 'User ID',
      fieldName: 'id',
      minWidth: 80,
      maxWidth: 120,
      isResizable: true
    },
    {
      key: 'title',
      name: 'Case Title',
      fieldName: 'title',
      minWidth: 150,
      maxWidth: 250,
      isResizable: true,
      onRender: renderEditableColumn
    },
    {
      key: 'userId',
      name: 'Case Number',
      fieldName: 'userId',
      minWidth: 80,
      maxWidth: 120,
      isResizable: true,
      onRender: renderCenteredColumn
    }
  ], [renderEditableColumn, renderCenteredColumn]);

  // 初始化数据
  useEffect(() => {
    const fetchData = async() => {
      const data = await fetchExampleData();
      console.log(data.length);
      setItems(data);
    };

    fetchData();
  }, []);

  return (
    <div className="my-table-container">
      <DetailsList
        items={items}
        columns={columns}
        setKey="set"
        ariaLabelForSelectionColumn="Toggle selection"
        ariaLabelForSelectAllCheckbox="Toggle selection for all items"
        checkButtonAriaLabel="select row"
      />
    </div>
  );
};

export default MyTable;

修复说明

  • 修正useEffect依赖后,不会再在编辑状态变化时重新拉取数据,避免覆盖用户修改。
  • 使用useCallback和useMemo确保渲染函数和列配置能响应状态变化,让DetailsList正确感知数据更新。
  • 移除不合理的key后,React能根据items和columns的变化自动触发组件重新渲染,同步显示更新后的值。

内容的提问来源于stack exchange,提问作者pifflox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 12:39:53