如何在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
相关产品推荐
相关产品推荐

