'State Not Updating when Consuming React Custom Hook that Uses Axios with useReducer to Get Async Data

My app returns a "null is not an object" Render Error when consuming the useFetch custom hook that I made to get async data using axios. The status, error, and data properties remains the same with their default values, I don't know if the problem is related to how I mutate my data or I'm not returning or consuming them properly.

Render Error

API

// Axios config

import axios from "axios";

const instance = axios.create({
    baseURL: "http://10.0.2.2:8000/api",
});

export default instance;
// Custom hook

import { useReducer, useEffect } from "react";
import axiosConfig from "../axiosConfig";

export default function useFetch(url) {
    const initialState = {
        status: "idle",
        error: null,
        data: null,
    };

    const [state, dispatch] = useReducer((state, action) => {
        switch (action.type) {
            case "FETCHING":
                return { ...state, status: "fetching" };
            case "FETCHED":
                return { ...state, status: "fetched", data: action.payload };
            case "FETCH_ERROR":
                return { ...state, status: "error", error: action.payload };
            default:
                return state;
        }
    }, initialState);

    useEffect(() => {
        let cancelRequest = false;
        if (!url) return;

        function fetchData() {
            dispatch({ type: "FETCHING" });

            axiosConfig
                .get(url)
                .then((response) => {
                    if (cancelRequest) return;
                    dispatch({ type: "FETCHED", payload: response.data });
                })
                .catch((error) => {
                    if (cancelRequest) return;
                    dispatch({ type: "FETCH_ERROR", payload: error });
                });
        }

        fetchData();

        return function cleanup() {
            cancelRequest = true;
        };
    }, [url]);

    return [state];
}
// Consume custom hook

import React from "react";
import {
    View,
    Text,
    StyleSheet,
    TouchableOpacity,
    Image,
    ActivityIndicator,
} from "react-native";
import GlobalStyles from "../constants/GlobalStyles";
import { Entypo, EvilIcons } from "@expo/vector-icons";
import format from "date-fns/format";
import useFetch from "../utils/hooks/useFetch";

export default function TweetScreen({ route, navigation }) {
    const [{ status, error, data: tweet }] = useFetch(
        `/tweets/${route.params.tweetId}`
    );

    error && console.log(error);

    function gotoProfile(userId) {
        navigation.navigate("Profile Screen", { userId });
    }

    return (
        <View style={GlobalStyles.container}>
            {status === "fetching" ? (
                <ActivityIndicator size="large" color="#007aff" />
            ) : (
                <>
                    <View
                        style={[
                            GlobalStyles.flexRow,
                            GlobalStyles.spaceBetween,
                            styles.tweetContainer,
                        ]}
                    >
                        <TouchableOpacity
                            style={GlobalStyles.flexRow}
                            onPress={() => gotoProfile(tweet.user.id)}
                        >
                            <Image
                                style={styles.avatar}
                                source={{ uri: tweet.user.avatar }}
                            />
                        </TouchableOpacity>
                    </View>
                </>
            )}
        </View>
    );
}


Solution 1:[1]

Just solved this thing, turns out I just needed to do return state instead of return [state] as wrapping it inside an array causes the state to be stale.

So now I just consume the custom hook like this: const { status, error, data: tweet } = useFetch(...);

Instead of: const [{ status, error, data: tweet }] = useFetch(...);

Solution 2:[2]

Your data response is null. You can avoid getting undefind by saying { uri : tweet?.data.avatar } , but I assume you should rather first destructure your useFetch correctly by doing const { data, error , loading } = useFetch(url) . If thats not going to help, then you should require(`${tweet?.data.avatar}`) . Hope that helps. Your code is really fragile though, you should debug first why you getting back null...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ColstonBod-oy
Solution 2 Audrius