1. React 状态更新机制
React 中的状态更新( setState )是 异步 的,这意味着:

// 修复前的问题代码
onChange={val => {
  const newSearchForm = { ...searchForm, teacher_name: val || undefined };
  setSearchForm(newSearchForm);  // ← 这里是异步更新
  setPage(1);
  fetchData(1, true);  // ← 这里调用时,searchForm 还是旧值!
}}

当 setSearchForm(newSearchForm) 被调用时, searchForm 状态并不会立即更新,而是在下一次渲染时才会更新。但紧接着调用的 fetchData(1, true) 仍然会使用旧的 searchForm 值。

2. useCallback 的闭包陷阱

fetchData 函数使用了 useCallback :

const fetchData = useCallback(async (pageNum: number = 1, reset: boolean = false) => {
  // 这里的 searchForm 是闭包中捕获的值
  const params = {
    page: pageNum,
    pageSize: 10,
    year: searchForm.year || null,  // 使用的是闭包中的旧值
    teacherArray: searchForm.teacher_name ? [searchForm.teacher_name] : null
  };
  // ...
}, [searchForm]);  // 依赖项只有 searchForm

闭包问题 : useCallback 创建的函数会"记住"创建时的 searchForm 值,即使在 onChange 中调用 setSearchForm 更新了状态, fetchData 函数内部仍然使用的是旧的 searchForm 值。

修复前:

// 问题代码
onChange={val => {
  const newSearchForm = { ...searchForm, teacher_name: val || undefined };
  setSearchForm(newSearchForm);  // 异步更新
  setPage(1);
  fetchData(1, true);  // 使用旧的 searchForm
}}

// fetchData 函数
const fetchData = useCallback(async (pageNum: number = 1, reset: boolean = false) => {
  const params = {
    year: searchForm.year || null,  // 旧值
    teacherArray: searchForm.teacher_name ? [searchForm.teacher_name] : null  // 旧值
  };
  // ...
}, [searchForm]);

修复后:

//  修复后的代码
onChange={val => {
  const newSearchForm = { ...searchForm, teacher_name: val || undefined };
  setSearchForm(newSearchForm);
  setPage(1);
  fetchData(1, true, newSearchForm);  // 直接传递新值
}}

// 修改后的 fetchData 函数
const fetchData = useCallback(async (pageNum: number = 1, reset: boolean = false, customSearchForm?: SearchFormType) => {
  // 使用传入的 customSearchForm 或当前的 searchForm
  const currentSearchForm = customSearchForm || searchForm;
  
  const params = {
    year: currentSearchForm.year || null,  // 使用最新值
    teacherArray: currentSearchForm.teacher_name ? [currentSearchForm.teacher_name] : null  // 使用最新值
  };
  // ...
}, [searchForm]);

我的解决方案是在 fetchData 函数中添加一个可选的 customSearchForm 参数,允许直接传入最新的搜索表单数据,而不依赖于可能还未更新的状态。
修改 fetchData 函数签名 :

const fetchData = useCallback(async (
  pageNum: number = 1, 
  reset: boolean = false, 
  customSearchForm?: SearchFormType  // 新增可选参数
) => {


使用最新的搜索表单数据 :

// 优先使用传入的 customSearchForm,否则使用当前状态
const currentSearchForm = customSearchForm || searchForm;

在 onChange 中传递新数据 :

onChange={val => {
  const newSearchForm = { ...searchForm, teacher_name: val || undefined };
  setSearchForm(newSearchForm);
  setPage(1);
  fetchData(1, true, newSearchForm);  // 直接传递新创建的表单数据
}}

不再依赖状态更新后的值,而是直接使用刚创建的 newSearchForm

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐