PART2 React Hooks Tutorial for Beginners: in the beginning there was componentDidMount (and render props)
Data fetching in React! Do you remember the old days of componentDidMount? You would slap fetch(url) in componentDidMount and call it a day. Here’s how to fetch an array of data from an API for rendering out a nice list:
- import React, { Component } from "react";
- export default class DataLoader extends Component {
- state = { data: [] };
- componentDidMount() {
- fetch("http://localhost:3001/links/")
- .then(response => response.json())
- .then(data =>
- this.setState(() => {
- return { data };
- })
- );
- }
- render() {
- return (
- <div>
- <ul>
- {this.state.data.map(el => (
- <li key={el.id}>{el.title}</li>
- ))}
- </ul>
- </div>
- );
- }
- }
You could even use async/await in componentDidMount, with some caveats. But most of the asynchronous logic in my projects would live outside React components. There are yet a couple of shortcomings in the above code.
The rendered list is fixed but with a render prop we can easily pass children as a function. The refactored component would look like the following:
- import React, { Component } from "react";
- export default class DataLoader extends Component {
- state = { data: [] };
- componentDidMount() {
- fetch("http://localhost:3001/links/")
- .then(response => response.json())
- .then(data =>
- this.setState(() => {
- return { data };
- })
- );
- }
- render() {
- return this.props.render(this.state.data);
- }
- }
And you would consume the component by providing a render prop from the outside:
- <DataLoader
- render={data => {
- return (
- <div>
- <ul>
- {data.map(el => (
- <li key={el.id}>{el.title}</li>
- ))}
- </ul>
- </div>
- );
- }}
- />
Even this pattern (born for providing a nicer alternative to mixins and HOCs) has its shortcomings. And that’s (I guess) the exact reason which lead React engineers to come up with hooks: provide a better ergonomics for encapsulating and reusing logic in React.
So impatient as I am, one of the first things I wanted to try with hooks was data fetching. But what hook I’m supposed to use for fetching data? Would the component still use the render prop pattern?
Let’s see into the next section!
React Hooks Tutorial for Beginners: fetching data with useEffect
I thought data fetching with React hooks shouldn’t look so different from useState. A quick glance at the documentation gave me an hint: useEffect could be the right tool for the job.
I read: “useEffect serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in React classes, but unified into a single API”
Bingo! It’s amazing, isn’t it? With this knowledge in hand I refactored the first version of Dataloader for using useEffect. The component becomes a function and fetch gets called inside useEffect. Moreover, instead of calling this.setState I can use setData (an arbitrary function extracted from useState):
- import React, { useState, useEffect } from "react";
- export default function DataLoader() {
- const [data, setData] = useState([]);
- useEffect(() => {
- fetch("http://localhost:3001/links/")
- .then(response => response.json())
- .then(data => setData(data));
- });
- return (
- <div>
- <ul>
- {data.map(el => (
- <li key={el.id}>{el.title}</li>
- ))}
- </ul>
- </div>
- );
- }
At this point I thought “what could be wrong?” and I launched the app. This is what I saw in the console:

It was clearly my fault because I’ve already got an hint of what was going on:
“useEffect serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount“
componentDidUpdate! componentDidUpdate is a lifecycle method running every time a component gets new props, or a state change happens.
That’s the trick. If you call useEffect like I did you would see an infinite loop. And for solving this “bug” you would need to pass an empty array as a second argument to useEffect:
- //
- useEffect(() => {
- fetch("http://localhost:3001/links/")
- .then(response => response.json())
- .then(data => setData(data));
- }, []); // << super important array
- //
I wish this info got the visibility it deserves rather than being at the end of this page: Using the Effect Hook. But even with this informations I wouldn’t suggest rewriting all your React components to use hooks for fetching. A lot could still change in the near future, as Ryan Florence suggests:
Anyway, useEffect replaces componentDidMount, componentDidUpdate, and componentWillUnmount, which I think is a nice thing for both experts developers and newcomers to React.

沒有留言:
張貼留言