网站搜索

reactjs - 调用 setState() 会执行函数值,而不是传递它


我有以下代码。按下按钮时,预期的行为是重新创建 AxiosInstance 并更新状态。但实际上,实例被重新创建后会在某处被执行。

function createApi(): AxiosInstance {
  const api = axios.create();
  // ...
  return api;
}

function App() {
  const [api, setApi] = React.useState<AxiosInstance>(() => createApi());
  const resetApi = () => {
    setApi(createApi());
  };

  console.log(api);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick={resetApi}>Reset API</button>
    </div>
  );
}

在 Typescript 中没有错误,setApi 在我的 IDE 中具有以下签名:

React.Dispatch<React.SetStateAction<AxiosInstance>>

这将被评估为类似这样的结果:

(value: AxiosInstance) => void

这正是我在第一个代码片段中所做的,那么为什么它不起作用呢?

答案#1

好吧,所以显然 TypeScript 对我撒谎了,它并不像那样简单。AxiosInstance 有以下签名:

export interface AxiosInstance {
  (config: AxiosRequestConfig): AxiosPromise;
  (url: string, config?: AxiosRequestConfig): AxiosPromise;
  ...
}

所以 AxiosInstance 是一个 function,useState 在底层使用了 useReducer,在深入研究 ReactJS 代码库后,我发现 useReducer 中使用的 dispatch 函数只是简单地检查接收到的值是否是一个通用函数

function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
  return typeof action === 'function' ? action(state) : action;
}

因此,dispatch 错误地将 AxiosInstance(它本身是一个函数)当作回调函数,因为它是一个函数,并直接执行它。

解决方法很简单,使用回调来返回实例,而不是直接传入 setState

setApi(() => createApi())

实时示例