calling setState() will execute the function value instead of passing it

Viewed 115

I have the following code. When pressing the button, the expected behavior is that the AxiosInstance is recreated and the state is updated. But in reality, the instance is recreated and then executed somewhere.

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>
  );
}

There is no error in Typescript, setApi has the following signature in my IDE:

React.Dispatch<React.SetStateAction<AxiosInstance>>

Which will be evaluated to something like this:

(value: AxiosInstance) => void

Which is exactly what I did in the first code snippet, so why does it not work?

1 Answers

Okay, so apparently Typescript has lied to me, it's not as simple as that. AxiosInstance has the following signature:

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

So AxiosInstance is a function, useState uses useReducer under the hood and after digging up the ReactJS codebase, I found out that the dispatch function used in useReducer only makes a simple check if the value received is a generic function

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

So the dispatch mistakes AxiosInstance (which is itself a function) for a callback because it's a function, and executes it directly.

The fix is pretty simple, make a callback to return the instance instead of passing directly in setState

setApi(() => createApi())

Live Example

Edit 64427195/calling-setstate-will-execute-the-function-value-instead-of-passing-it

Related