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)
}
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>
);
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>
)
}
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)