1

I have the following custom hook in a .tsx file:

import {useCallback, useState} from 'react';

const useHttp = (requestObj: any, setData: Function) => 
{
    const [isLoading, setIsLoading] = useState(false); 
    const [error, setError] = useState(null);
    const [elements, setElements] = useState(null);
    
    const sendRequest = useCallback(() =>
    {
        setIsLoading(true);
        setError(null);
        fetch(requestObj.url)
            .then(res => res.json())
            .then(data => {
                setIsLoading(false);
                setData(data);
                let jsx = (<div>Testing conor</div>)
                setElements(jsx);
            })
            .catch(err => 
            {
                setError(err.message);
                setIsLoading(false);
                console.log('There was an error');
            });
    }, []);

    return {
        isLoading: isLoading,
        error: error,
        elements: elements,
        sendRequest: sendRequest
    }
} 

export default useHttp;

When I call setElements my IDE is saying there is an error when I call setElements.

let jsx: JSX.Element
Argument of type 'Element' is not assignable to parameter of type 'SetStateAction<null>'.
  Type 'Element' provides no match for the signature '(prevState: null): null'.ts(2345)

the code works fine so I jst want to know why the ide is saying its a problem

discodowney
  • 1,475
  • 6
  • 28
  • 58
  • [Don't put JSX into the state.](https://stackoverflow.com/a/47875226/1218980) – Emile Bergeron Apr 21 '21 at 18:09
  • @EmileBergeron im making a custom hook for http requests. If it fails I want a modal to show I want the hook to be responsible for making that modal so every where the hook is used the component that uses it doesnt have to – discodowney Apr 21 '21 at 18:23
  • It's not how it should be done. Context and Provider would make more sense in this case, since the provider could inject the JSX based on the current context (http stuff) and the hook could just be a context consumer/setter. – Emile Bergeron Apr 21 '21 at 18:26
  • @EmileBergeron sorry if what im about to say is silly, im brand new at react so please bear with me. In that case, on the context, I would need a specific field for each http call or an array of errors or something, since i have multiple components on the screen on load and if a few of them fail i would need to show all the errors. Do i have that correct? – discodowney Apr 21 '21 at 18:42
  • You have a good point on the complexity of handling all the different possible requests, and you have a kind of XY problem where a generic request hook is probably not the solution, but we'd need more info on the actual use-case to tell you more. If you want to show a modal on fetch error, maybe a simple errors array in the context would be enough to show a generic modal that informs the user of a failure. – Emile Bergeron Apr 21 '21 at 18:52
  • The use case is pretty much as you state there. It is simply to inform a user that the http call failed and then show the error message explaining why. If a generic request hook is not the answer, what would be the thing to look at? I thought the hook cos I can make the http logic in one spot and then used all over. – discodowney Apr 21 '21 at 18:55
  • That said, we're kind of going off-track of your initial question. In the end, Nicholas provided a quick solution, and you can use whatever works for you. I was just informing you (and any future readers) about the potential anti-pattern you're going for here. For a simple `div`, it might never be a problem, but for a full fledged modal, with it's own lifecycle, being stored in a state will mess up the default React lifecycle for these elements (not mounted/unmounted and/or updated at predictable times, etc) – Emile Bergeron Apr 21 '21 at 18:56
  • The hook for the common request logic is fine, it's even what it's meant to be used for. – Emile Bergeron Apr 21 '21 at 18:57
  • Another possible path to explore: [error boundaries](https://reactjs.org/docs/error-boundaries.html). This could be used side-by-side with a custom request hook. – Emile Bergeron Apr 21 '21 at 19:00
  • Here's an [example of a global modal with custom content](https://stackoverflow.com/a/62503461/1218980) through the context. – Emile Bergeron Apr 21 '21 at 19:08
  • Another interesting thread: https://stackoverflow.com/q/35623656/1218980 – Emile Bergeron Apr 21 '21 at 19:10

1 Answers1

3
const [elements, setElements] = useState(null);

Since you havn't given an explicit type to this state, typescript will infer it from usage. It sees you initialized the state with null, and so it assumes the state is (and always will be) null

To let the state change to something ther than null, you'll need to be explicit about the type. If you're storing elements in state, then that type would be:

const [elements, setElements] = useState<JSX.Element | null>(null)
Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98