13

I've got the following React function (using the alpha of React Hooks) that has a TypeScript error (thought it still works). The error is pointing to the line of code where the axios promise completes with .then(a=>{setData(a))... It is complaint about a I think.

TS2345: Argument of type 'void | AxiosResponse' is not assignable to parameter of type 'SetStateAction'.   Type 'void' is not assignable to type 'SetStateAction'.

Here is a link to the code: https://codesandbox.io/s/7zyx4vv6k6

Is there more that should be typed in this file? I'm new to TypeScript and was suprised how little I needed to do to make it only complain about this one thing. In codesandbox it does not show me the tpyescript error that I can see. Is there a way to make it show up there like it does in my webstorm IDE? In codesandbox, the line I want to make type correct is 32 even though it does not show it in codesandbox as an error

import { useState, useEffect } from "react";
import axios from "axios";

const useAxiosFetch = (url: string, timeout: number) => {
  const [data, setData] = useState(null);
  const [error, setError] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let unmounted = false;
    let source = axios.CancelToken.source();
    axios
      .get(url, {
        cancelToken: source.token,
        timeout: timeout,
      })
      .catch(function(e) {
        if (!unmounted) {
          setError(true);
          setErrorMessage(e.message);
          setLoading(false);
          if (axios.isCancel(e)) {
            console.log(`request cancelled:${e.message}`);
          } else {
            console.log("another error happened:" + e.message);
          }
        }
      })
      .then(a => {
        if (!unmounted) {
          setData(a);
          setLoading(false);
        }
      });
    return function() {
      unmounted = true;
      source.cancel("Cancelling in cleanup");
    };
  }, []);

  return { data, loading, error, errorMessage };
};

export default useAxiosFetch;

1 Answer 1

16

The reason is in this line:

const [data, setData] = useState(null)

Because there is no type parameter provided explicitly for useState, TypeScript infers the type of data based on the type of initial state. In this case, the initial state is null and TypeScript treats this type as the only possible state.

You know that the state will either be null or something else — but TypeScript doesn't. Let's use type parameters to tell it what's really going on.

const [data, setData] = useState<AxiosResponse | null | void>(null);

This gets rid of the error, but looks strange — why is void there? The reason for that is that you catch before you then — and since catch emits a side effect (returns void, in other words), the type propagated to catch is either void or AxiosResponse. Let's fix that by replacing then and catch.

The final solution:

import axios, { AxiosResponse } from "axios";
import { useEffect, useState } from "react";

const useAxiosFetch = (url: string, timeout: number) => {
  const [data, setData] = useState<AxiosResponse | null>(null);
  const [error, setError] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let unmounted = false;
    const source = axios.CancelToken.source();
    axios
      .get(url, {
        cancelToken: source.token,
        timeout,
      })
      .then(a => {
        if (!unmounted) {
          setData(a);
          setLoading(false);
        }
      })
      .catch(function(e) {
        if (!unmounted) {
          setError(true);
          setErrorMessage(e.message);
          setLoading(false);
          if (axios.isCancel(e)) {
            console.log(`request cancelled:${e.message}`);
          } else {
            console.log("another error happened:" + e.message);
          }
        }
      });

    return function() {
      unmounted = true;
      source.cancel("Cancelling in cleanup");
    };
  }, []);

  return { data, loading, error, errorMessage };
};

export default useAxiosFetch;

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @KarolMajewski. Just curious if you know what it takes for codesandbox to show typescript errors? Also, I assume that since my webstorm ide did not complain, TypeScript is happy with everything else. true?
No problem! I don't know about CodeSandbox, but I know StackBlitz does that. I prepared an example for you: stackblitz.com/edit/react-ts-bumnp9. And yes, it seems to be fine now. ;-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.