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

如何在TypeScript中通过React Hooks正确使用previousState

在TypeScript React中使用React Hooks获取前状态(previousState)的正确方式

问题根源

你的代码出现类型错误,是因为**prevCountRef的泛型类型定义错误**:你把它声明为HTMLDivElement|null,但实际要存储的是number类型的count状态,类型不匹配导致报错。

基础场景的修正(计数器例子)

直接修正useRef的类型,让它匹配状态的类型:

import { useEffect, useRef, useState } from "react";

export function Counter() {
  const [count, setCount] = useState(0);
  // 将泛型改为number | undefined,初始值设为undefined
  const prevCountRef = useRef<number | undefined>(undefined);

  useEffect(() => {
    // 每次count更新后,把当前值存入ref作为下一次的前状态
    prevCountRef.current = count;
  }, [count]);

  return (
    <h1>
      Now: {count}, before: {prevCountRef.current ?? 'N/A'}
      <button onClick={() => setCount((count) => count + 1)}>Increment</button>
    </h1>
  );
}

封装通用的usePrevious Hook

为了避免重复写useRef和useEffect,可以封装一个通用Hook,自动处理类型推导,复用性更强:

import { useEffect, useRef } from "react";

function usePrevious<T>(value: T): T | undefined {
  const ref = useRef<T | undefined>();

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}

用这个Hook重构计数器例子:

import { useState } from "react";
import usePrevious from './usePrevious';

export function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);

  return (
    <h1>
      Now: {count}, before: {prevCount ?? 'N/A'}
      <button onClick={() => setCount((count) => count + 1)}>Increment</button>
    </h1>
  );
}

在FluentUI DetailsList删除场景中的应用

针对你提到的FluentUI DetailsList处理项删除的场景,用usePrevious可以轻松对比前后的列表数据,找到被删除的项并执行后续逻辑:

import { useEffect, useState } from "react";
import { DetailsList, SelectionMode } from "@fluentui/react";
import usePrevious from './usePrevious';

// 定义列表项类型
interface ListItem {
  key: string;
  name: string;
  description?: string;
}

export function ManageableList() {
  const [items, setItems] = useState<ListItem[]>([
    { key: "1", name: "文档A", description: "项目需求文档" },
    { key: "2", name: "文档B", description: "技术设计文档" },
    { key: "3", name: "文档C", description: "测试报告" }
  ]);
  // 获取上一次的列表数据
  const prevItems = usePrevious(items);

  // 监听列表变化,处理删除逻辑
  useEffect(() => {
    // 只有当前列表长度小于之前的长度时,说明有项被删除
    if (prevItems && items.length < prevItems.length) {
      const deletedItem = prevItems.find(
        item => !items.some(current => current.key === item.key)
      );
      
      if (deletedItem) {
        // 这里可以添加删除后的业务逻辑,比如显示提示、记录日志等
        console.log(`已删除项:${deletedItem.name}`);
        // 示例:显示删除提示
        // alert(`"${deletedItem.name}" 已被删除`);
      }
    }
  }, [items, prevItems]);

  // 删除项的回调函数
  const handleDeleteItem = (itemKey: string) => {
    setItems(prev => prev.filter(item => item.key !== itemKey));
  };

  // 定义DetailsList的列配置
  const columns = [
    {
      key: "name",
      name: "名称",
      fieldName: "name",
      minWidth: 150,
      isResizable: true
    },
    {
      key: "description",
      name: "描述",
      fieldName: "description",
      minWidth: 200,
      isResizable: true
    },
    {
      key: "actions",
      name: "操作",
      minWidth: 100,
      onRender: (item: ListItem) => (
        <button 
          onClick={() => handleDeleteItem(item.key)}
          style={{ padding: '4px 8px', cursor: 'pointer' }}
        >
          删除
        </button>
      )
    }
  ];

  return (
    <div style={{ padding: '16px', maxWidth: '800px' }}>
      <h2>文档列表</h2>
      <DetailsList
        items={items}
        columns={columns}
        selectionMode={SelectionMode.none}
        enableShimmer={false}
      />
    </div>
  );
}

关键要点

  • 类型匹配:确保useRef的泛型类型和要存储的状态类型一致,避免类型不兼容错误。
  • 通用Hook复用:封装usePrevious可以减少重复代码,同时利用TypeScript的泛型自动推导类型,提升开发效率。
  • 状态对比逻辑:在处理删除等场景时,通过对比前后状态的差异,可以精准定位变化的内容,执行对应的业务逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 16:20:59