🏋🏽‍♂️ Hooks Workout #3: Fetching Data with Hooks

🏋🏽‍♂️ Hooks Workout #3: Fetching Data with Hooks

Hellloooo!

Did you think the last lesson was fun? Today’s lesson may as well blow your mind!

In today’s lesson, we’ll be fetching data from a remote server and displaying the information received within the application you’ll build.

So, are you ready?


This lesson is a part of the Hooks workout series:

  1. Your First React Hooks Application  
  2. Advanced Hooks
  3. Fetching Data with Hooks  👈🏽 You're here
  4. Custom Reusable Hooks
  5. Moving into Production

Choosing the right hook for the job

We’ve had a look at three hooks so far.

Of these three hooks, which do you think is more suited to fetching data from a remote server?

I’ll let you have a minute to think about the answer to the question.

🤔

Got it now?

useMemo is used to memoize a value. Definitely not that.

useState is used to create local state within a component. This is important for the application, but it isn’t the where we’ll perform the actual data fetch.

If you answered, useEffect, then you’re perfectly correct.

useEffect is the perfect hook for side effects — including fetching data from a remote server.

Fetching Data on Mount

Your first exercise will be fetching data from a remote server as soon as the App component mounts.

This is similar to how you’d have fetched data using componentDidMount — if you’re familiar with that class lifecycle method.

Here’s how you’ll build this:

  1. Have a state variable user created within the component.
  2. Let the initial state value be represented by the variable, user. This will default to {data: null}
  3. Fetch the user data using the server URL: https://randomuser.me/api/
  4. Upon receiving data from the server, update the value of the state user variable.
  5. Render whatever the user state variable is as stringified JSON.

Enough talking. Let me help you get this app built.

Start a new project here: https://codesandbox.io/s/new

Write the following:

// don't forget to import useState on Line 1
// then write the following: 

function App() {
  const stringifyData = data => JSON.stringify(data, null, 2);
  const initialData = stringifyData({ data: null });
  const [user, setUser] = useState(initialData);

  return (
    <div className="App">
      <h2> User Data: </h2>
      <section>
        <pre>{user}</pre>
      </section>
    </div>
  );
}

Written that out?

If so, let’s move on.

First, we created a state variable called user, and the useState call is passed an initial state value called initialData :

const [user, setUser] = useState(initialData);

The user state value is then rendered within the returned markup of App

...
<pre> {user} </pre> 
...

But, what’s initialData?

The data to be received from the server will be in JSON format. Before we get this data from the server, we can set the initial value to {data: null}.

Since we can’t render objects within JSX, convert this value to a string:

 ... 
const stringifyData = data => JSON.stringify(data, null, 2);

JSON.stringify takes a JSON object and converts it to a string. The last argument, 2, is used to insert white space into the JSON string for readability purposes.

Now, that’s it.

Go over the code one more time. You should understand it fully now.

Here’s the result you should have on your screen now:

The default state value should be stringified and rendered.

Let’s perform the actual data fetch.

To do this, we will define and invoke a fetchData function within the useEffect hook.

Go ahead and write this within your App component:

// write this below the useState call
// before the return statement. 

useEffect(() => {

    const fetchData = () => {
      const uri = "https://randomuser.me/api/";
      
      fetch(uri)
        .then(res => res.json())
        .then(({ results }) => {
          const { name, gender, dob } = results[0];
          const dataVal = stringifyData({
            ...name,
            gender,
            age: dob.age
          });

          setUser(dataVal);
        });
    };

    fetchData();
  }, []);

Written that?

The first line within the useEffect function defines a new function, fetchData.

const fetchData = () => {
      ...
};

The function uses the fetch API to retrieve data from a remote server.

...
const uri = "https://randomuser.me/api/";
      
fetch(uri).then(res => res.json())
...

Upon receiving the results from the server, the name, gender and dob of the user is deconstructed from the data.

const { name, gender, dob } = results[0];

We then create a new stringified object called dataVal . This contains the name, gender and date of birth of the user.

const dataVal = stringifyData({
    ...name,
    gender,
    age: dob.age
});

Finally, we set the new state value to this stringified object — constructed from the data received from the server.

setUser(dataVal);

At the bottom of the effect function, the fetchData function is then invoked:

fetchData()

This triggers the flow within the fetchData function.

At the very bottom of the code block, you’ll find an array dependency. Have a look:

useEffect(() {
  // ... all the fetchData logic
}, []) // 👈 Note empty array passed here

As we saw in the last lesson, useMemo receives an array dependency to prevent the function from being invoked on every render.

It’s the same with useEffect and many other hooks. In this example, passing an empty array dependency means the useEffect function will only be invoked ONCE - when the component mounts.

Consequently, the data is fetched on mount. How beautiful!

If you did write the code above, you should now have a result similar to this:

The user’s title, first and last name, gender and age printed to the screen.

Your Turn

Now it’s your turn to build something even more beautiful.

In the example we considered above, the data was fetched as soon as the App component was mounted.

Can you try something different?

  1. Have two buttons at the top of the page that read, “fetch male user” and “fetch female user”.
  2. By default, fetch a female user when the App mounts.
  3. Upon clicking any of the buttons, trigger a new data fetch from the server. e.g. If a user clicks the “fetch male user” button, go ahead and fetch a new male user.
A sample solution

Can you do this?

Here are a few hints.

1. To “re-fetch” users, you need to modify the array dependency for the useEffect call. Something like this:

useEffect(() => {
 // ... your fetch logic
}, [gender])

Where gender represents either male or female, and changes when any of the buttons is clicked.

2. The generic api to fetch a user is [https://randomuser.me/api/]

To streamline the results to either a male or female user you need to append a gender url parameter as shown below:

'https://randomuser.me/api/?gender=' + gender

Where gender could be either male or female.

You’ve got this!

Go ahead. Be confident. Do the exercise. It’s the only way to truly master the concepts you’ve learned.

NB: Don’t be scared of not getting the exercise right. You’re better off getting it wrong that NOT attempting the exercise.

I’ll share my solution in the next lesson so you can compare with yours.

Say No to Javascript Fatigue

At Devcher, we're changing how you stay up to date with Javascript by building the principal destination for short-form developer mobile video. Try Devcher.

Join Devcher: The Anti Fatigue Community

Comments

Become a Devcher member below to join the conversation. As a member, you will also receive new posts by email (you can unsubscribe at any time).