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

React Hook中checkErrors返回值与内部输出不一致问题求助

函数内部输出与外部返回值不一致问题

问题描述

调用checkErrors函数时,函数内部console.log输出结果为[{ foo: 'bar'}],但外部接收返回值时得到的是空数组[],调用发生在包含useState的React Hook(useField)中,完全不清楚不一致的原因。

伪代码示例:

// 实际调用的函数示例,伪代码便于理解问题
const checkErrors = (input) => {
  const data = invokeCheckFunction(input)
  console.log('wtf ==>', data) // [{ foo: 'bar'}]
  return data
}

调用方代码:

const test = checkErrors(myInput)
console.log('why is it empty', test) // []

调用方仅使用了useState,找不到这种莫名不同步的原因。

错误检查相关代码

错误检查内部处理函数

import type { CheckFunction, Config, FieldError } from '../types'

type ErrorWithInternal<T, E = Error> = {
  INTENRAL_IS_ERROR: true
} & FieldError<T, E>

export const invokeCheckFunction = <T, C extends CheckFunction<T>, E = Error>(
  value: T,
  name: string,
  checkFunction: C,
) => {
  let result: ReturnType<C> | ErrorWithInternal<T, E>
  try {
    result = checkFunction(value)
  } catch (error) {
    const fieldError: ErrorWithInternal<T, E> = {
      error: error as E,
      message: (error as Error)?.message,
      name,
      value,
      INTENRAL_IS_ERROR: true,
    }
    result = fieldError
  }
  return result
}

主处理函数

const isError = <T, C extends CheckFunction<T>, E = Error>(
  error: ErrorWithInternal<T, E> | ReturnType<C>,
): error is ErrorWithInternal<T, E> => !!(error as ErrorWithInternal<T, E>)?.INTENRAL_IS_ERROR

const isPassed = <T, C extends CheckFunction<T>, E = Error>(
  error: ErrorWithInternal<T, E> | ReturnType<C>,
): error is ReturnType<C> => !isError(error)

export const checkErrors = <T, C extends CheckFunction<T>, E = Error>(
  value: T,
  name: string,
  checkFunctions: C[],
): [FieldError<T, E>[], ReturnType<C>[]] => {
  const results = checkFunctions.map((checkfn) =>
    invokeCheckFunction<T, C, E>(value, name, checkfn),
  )

  const internalErrors: ErrorWithInternal<T, E>[] = results.filter((e) => isError(e))
  const passed = results.filter((e) => isPassed(e)) as ReturnType<C>[]
  const errors: FieldError<T, E>[] = internalErrors.map((e) => {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { INTENRAL_IS_ERROR, ...rest } = e
    return rest as FieldError<T, E>
  })

  console.log('wtf => ', errors, passed)
  return [errors, passed]
}

问题发生的Hook逻辑

export const useField = <T, E = Error, C extends CheckFunction<T> = CheckFunction<T>>(
  name: string,
  initialValue: T,
  configOrCheckFunction?: C | Partial<Config>,
  ...checkFunctions: C[]
) => {
  const [value, setValue] = useState<T>(initialValue)
  // 注意:这不是useState,避免混淆语法
  const [initialErrors, initialCheckResults] = checkErrors<T, C, E>(value, name, checks, config)
  const [errors, setErrors] = useState<FieldError<T, E>[]>(initialErrors)
  const [checkResults, setCheckResults] = useState(initialCheckResults)

  const onChange = (event: ChangeEvent<HTMLInputElement>) => {
    const { value: newVal } = event.target
    const [newErrors, newChecked] = checkErrors<T, C, E>(
        newVal as T,
        name,
        checkFunctions,
    )
    console.log('why is it empty', newErrors)
    setErrors(newErrors)
    setValue(newVal)
  }
}

为了可读性省略了无关代码,核心逻辑如上,找不到问题原因。

测试用React应用代码

import { useField } from 'eformless'

const App = () => {
  // 示例错误处理函数
  const nonEmpty = (val: string) => {
    if (val.length === 0) {
      throw new Error('Cannot be empty')
    }
    return val
  }

  const test = useField('example', '', nonEmpty)

  return (
    <input name={test.name} value={test.value} onChange={test.onChange} />
  )
}

问题截图

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 04:32:31