How to trigger data fetching with React hooks?
© https://reactjs.org/

How to trigger data fetching with React hooks?

Fetch data with useEffect Hooks programmatically/manually

ByMario Kandut

honey pot logo

Europe’s developer-focused job platform

Let companies apply to you

Developer-focused, salary and tech stack upfront.

Just one profile, no job applications!

In many applications fetching data is done by triggering a React hook programmatically or manually. This article gives an example on how to do this. The used example is from the previous article How to fetch data with react hooks, below is the code:

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(
        'https://hn.algolia.com/api/v1/search?query=react',
      );
      const json = await res.json();
      setData(json.hits);
    };
    fetchData();
  }, []);

  return (
    <ul>
      {data.map(item => (
        <li key={item.ObjectId}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;

Fetch data programmatically with useEffect

The above example fetches data when the component mounts. We are always fetching the exact same query "react". Let's add an input element to fetch other stories. Therefore, we have to add an input field, a state for the search query from the input field and update fetchData to use this dynamic search query.

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);
  const [query, setQuery] = useState('react');

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(
        `https://hn.algolia.com/api/v1/search?query={query}`,
      );
      const json = await res.json();
      setData(json.hits);
    };
    fetchData();
  }, []);

  return (
    <React.Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <ul>
        {data.map(item => (
          <li key={item.ObjectId}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </React.Fragment>
  );
}

export default App;

💰 The Pragmatic Programmer: journey to mastery. 💰 One of the best books in software development, sold over 200,000 times.

One last thing. Try to update the code and run it. Did you notice something? When you type in the input field, the search is not triggering the fetchData(). The reason is that the useEffect hook does not depend on anything. The second argument of useEffect is an empty array, so it is only triggered once when the component mounts. The effect should run, when the search query changes. Hence, we have to provide the query as the second argument.

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);
  const [query, setQuery] = useState('react');

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(
        `https://hn.algolia.com/api/v1/search?query={query}`,
      );
      const json = await res.json();
      setData(json.hits);
    };
    fetchData();
  }, [query]);

  return (
    <React.Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <ul>
        {data.map(item => (
          <li key={item.ObjectId}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </React.Fragment>
  );
}

export default App;

Now on every change of the input field data is fetched. What if we want to add a Search button to avoid fetching on every key stroke?

Fetch data manually with useEffect

To avoid fetching data on every change of the query state, we can simply add a button to trigger fetchData(). Let's try it. We add a button with an onClick method to set a new state search, which triggers the useEffect hook.

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);
  const [query, setQuery] = useState('react');
  const [search, setSearch] = useState('react');

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(
        `https://hn.algolia.com/api/v1/search?query={query}`,
      );
      const json = await res.json();
      setData(json.hits);
    };
    fetchData();
  }, [search]);

  return (
    <React.Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button onClick={() => setSearch(query)}>Search</button>
      <ul>
        {data.map(item => (
          <li key={item.ObjectId}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </React.Fragment>
  );
}

export default App;

Well, now it works. Somewhat. We have two states to manage the search query. We have to refactor the code to remove duplication. Instead of setting an additional search state, which is the same as the query state, we can just provide the url with the query to fetch.

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);
  const [query, setQuery] = useState('react');
  const [url, setUrl] = useState(
    'https://hn.algolia.com/api/v1/search?query=react',
  );

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(url);
      const json = await res.json();
      setData(json.hits);
    };
    fetchData();
  }, [search]);

  return (
    <React.Fragment>
      <input
        type="text"
        value={query}
        onChange={event => setQuery(event.target.value)}
      />
      <button
        onClick={() =>
          setUrl(`https://hn.algolia.com/api/v1/search?query={query}`)
        }
      >
        Search
      </button>
      <ul>
        {data.map(item => (
          <li key={item.ObjectId}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </React.Fragment>
  );
}

export default App;

That's if for the implicit programmatic data fetching with useEffect. You can decide on which state the effect depends. Once you set this state, the useEffect will run again. In this case, if the URL state changes, the effect runs again to fetch data from the API.

TL;DR

Thanks for reading and if you have any questions, use the comment function or send me a message @mariokandut.

If you want to know more about React, have a look at these React Tutorials.

References (and Big thanks):

ReactJS, Robin Wieruch, Dave Ceddia

Scroll to top ↑