skip to Main Content

I’m new to React and Typescript and am learning about React hooks.
I however ran in to a problem which I don’t really understand/know how to fix.

Hook (for fetching data):

export const useFetch = <T>(
    fetchFunc: () => Promise<Response>,
    initialValue: T
) => {
    const [value, setValue] = useState<T>(initialValue);

    const fetchData = () =>
        fetchFunc()
            .then((res) => {
                if (res.ok) return res;
                throw new HttpFetchError(res);
            })
            .then(async (x) => await x.json())
            .then((x) => setValue(x as T));

    return [value, fetchData] as const;
};

I have a static class that fetches from the API (if called normally, everything is working fine here):

export class TestFetchClient {
    static client?: HttpFetchClient;

    static Init = (baseUrl?: string) =>
        (this.client = new HttpFetchClient(baseUrl, "/api/"));

    static async GetBored() {
        if (this.client == null) throw new Error("Init first!");

        return await this.client.Request({
            request: "activity",
            method: "GET",
        });
    }
    // ...
}

When I try to use my useFetch hook I get the error in the function GetBored() that "this" is undefined when trying to access the initialized client. So I guess somehow the GetBored() method is called without the context of the Class or something. Can somebody explain this behavior and/or provide a solution so I can access my client variable in this situation?

Usage:

export default function SamplePage2() {
    const [value, fetchValue] = useFetch(TestFetchClient.GetBored, {});

    useEffect(() => {
        // this throws the described error
        fetchValue();

        // this is working fine
        TestFetchClient.GetBored().then((x) =>
            x.json().then((y) => console.log(y))
        );
    }, []);

    useEffect(() => {
        console.log(value);
    }, [value]);

    return <h1>This is SamplePage 2</h1>;
}

Error message:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'client')
    at GetBored (TestFetchClient.ts:10:1)
    at fetchData (useFetch.ts:30:1)
    at SamplePage2.tsx:12:1

2

Answers


  1. Chosen as BEST ANSWER

    As motto pointed out in his comment, I should not use "this" for static classes but the real class name. "this" shouldn't work here in the first place.

    Solution:

    export class TestFetchClient {
        static client?: HttpFetchClient;
    
        static Init = (baseUrl?: string) =>
            (HttpFetchClient.client = new HttpFetchClient(baseUrl, "/api/"));
    
        static async GetBored() {
            if (HttpFetchClient.client == null) throw new Error("Init first!");
    
            return await HttpFetchClient.client.Request({
                request: "activity",
                method: "GET",
            });
        }
        // ...
    }
    

  2. I think you want something like this:

    export class TestFetchClient {
        static client?: HttpFetchClient;
    
        static Init = (baseUrl?: string) =>
            (TestFetchClient.client = new HttpFetchClient(baseUrl, "/api/"));
    
        static async GetBored() {
            if (TestFetchClient.client == null) throw new Error("Init first!");
    
            return await this.client.Request({
                request: "activity",
                method: "GET",
            });
        }
        // ...
    }
    

    Then you’d have to alter your usage to be (note the call to Init so that it creates your HttpFetchClient):

    export default function SamplePage2() {
        const [value, fetchValue] = useFetch(TestFetchClient.GetBored, {});
    
        useEffect(() => {
            // this throws the described error
            fetchValue();
    
            // this is working fine
            TestFetchClient.Init().GetBored().then((x) =>
                x.json().then((y) => console.log(y))
            );
        }, []);
    
        useEffect(() => {
            console.log(value);
        }, [value]);
    
        return <h1>This is SamplePage 2</h1>;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search