DEV Community

Francisldn
Francisldn

Posted on

React Data Fetching Patterns - Part II

This article is a continuation of:

React Data Fetching Patterns - useState-useEffect

Click here to access the Github repo for the code examples discussed below.

Custom Hook using React Context API

To deal with the problem of prop-drilling, we could create a custom hook which utilises React Context API. The useState-useEffect data fetching logic can then reside within this hook.

Using the example below, the UserContext component will create the users state, while UserProvider stores the states and provides them to other components through useContext hook.

interface UserProps {
    users: User[],
    loading: boolean,
    error: boolean,
}

export const UserContext = createContext<UserProps>({
    users: [],
    loading: false,
    error: false
})

export const UserProvider = ({children}: {children: React.ReactNode}) => {
    const [users, setUsers] = useState<User[]>([])
    const [error, setError] = useState(false)
    const [loading, setLoading] = useState(false)

    useEffect(() => {
        setLoading(true)
        getUsers(apiURL)
            .then(({results:data}) => {
                console.log({data})
                setUsers(
                    data.map((user:UserApi) => {
                        return {
                            id: user.id.value,
                            firstName: user.name.first,
                            lastName: user.name.last,
                            username: user.login.username,
                            email: user.email,
                        }})
                    )
                })
            .catch(() => setError(true))
            .finally(() => setLoading(false))
    },[])

    // useMemo hook is used to `memoize` the value so that they only recompute when states have changed
    const value = useMemo(() => ({
        users,
        loading,
        error
    }),[users, loading, error])

    return (
        <UserContext.Provider value={value}>
          {children}
        </UserContext.Provider>
      )
}
// Provide access to other components through useUsers custom hook
export function useUsers() {
    return useContext(UserContext)
}
Enter fullscreen mode Exit fullscreen mode

To provide access of the states to all the child components, we need to wrap the UserProvider around the top level component (<App/>).

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <UserProvider>
      <App />
    </UserProvider>
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

For a React component to access the states from UserProvider, the custom hook can be imported, and the required data can be retrieved through the custom hook, as illustrated below. This will allow any React component to have access to a full or subset of the states without having to prop-drill.

import { useUsers } from '../hooks/useUsers';

export default function CardList() {
    const {users, loading, error} = useUsers()

  return (
    <div className="grid sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 justify-center grid-cols-1 mt-8">
        {loading 
            ? <Loading />
            : error 
            ? <ErrorFallBack />
            : users.map((user:User) => (<CardItem key={`${user.id}${user.email}`} user={user}/>))
        }
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Pros

  • Easy to setup: With the Context API, it is relatively easy to set up and use
  • Easy to understand: The simple setup means that the code is also easily understandable

Cons

  • Unnecessary re-rendering: React will re-render all components whenever there is an update to the value of the props. For a larger app with thousands of components, this will slow down the performance of the app.

To deal with the issue of unnecessary component re-rendering and improve the performance of the app, let's proceed to look at the next data fetching method - Redux.

To be continued:

For more info on React Context, see here.

If you like the content, please hit the like button so that it can reach more people.

Top comments (0)