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;
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With