Skip to content

Chapter 6

Nov. 15

Chapter 6 summary

06_01 Fetching data with Hooks

  • Utilize the useState hook to create state variables for holding the fetched data, initializing it as null.
 const [data, setData] = useState(null);
  • Employ the useEffect hook to manage the data-fetching process.
  • Inside useEffect, make an HTTP request to the GitHub API using the fetch function to retrieve data for a specified user (e.g., “moonhighway”).
useEffect(() => {
   fetch(`https://api.github.com/users/moonhighway`)
     .then((response) => response.json())
     .then(data => setData (data)); // shorthand here is .then(setData)
 }, []);
    • Convert the API response to JSON format with .then(response => response.json()).
  • Store the fetched data in the state variable using setData(data).
  • Ensure a single API request upon component rendering by providing an empty dependency array to useEffect.
  • Display the fetched data as preformatted JSON within a pre tag.
if (data) return <pre>{JSON.stringify(data, null, 2)}</pre>;
  • This example showcases how to practically employ the useEffect hook for side effects like data fetching in a React component.

 

06_02 Displaying data from an API

  • Create a new function that will display some of the data for us:
function GitHubUser() {
  return (
    <div>
      <h1></h1>
    </div>
  )
}
  • Change the return in our App() to return a new component called GitHubUser and pass in the name property from data
 if (data) 
 return (
    <GitHubUser name={data.name} />
  );
  • Add {name} as a parameter to the GitHubUser function (an example of destructuring). Also add it inside the <h1>
function GitHubUser({ name, location, avatar }) {
  return (
    <div>
      <h1>{name}</h1>
      <p>{location}</p>
      <img src={avatar} height={150} alt={name}/>
    </div>
  );
}
  • Now add location within the GitHubUser component
    • <p>{location}</p> after the <h1>
    • Pass it in to GithubUser as a destructuring value

      function GithubUser({ name, location }) {
    • Add it to the return <GithubUser /> JSX, so
 <GithubUser name={data.name} location={data.location} />
  • Now add avatar as an <img>
    • {name, location, avatar} pass this in the GithubUser function
    • avatar={data.avatar_url} use this in <GithubUser />
    • After this, add a height to <img /> of 150
    • And lastly add an alt to the <img /> (use {name})
<img src={avatar} height={150} alt={name}/>

06_03 Handling loading states

Start to End summary: From loading the data and only checking whether it’s there, we go further by adding state hooks for loading, success and error to best handle our asynchronous request (calling the github API).

  • Here are states that we’d want to know when loading data:
    • A loading state
    • A success state
    • An error state
  • All of these can be done with useState! Add these consts to get them set up:
    • const [data, setData] = useState(null);
    • const [error, setError] = useState(null);
    • const [loading, setLoading] = useState(false);
  • Need to put these inside our component.
    • Within useEffect()
      • setLoading(true); // at the top, right after useEffect(() => {
    • Chain another .then to switch the loading state back
      • .then(() => setLoading(false));
    • Chain a .catch after that to so we can catch any error (by setting the state for error)
      • .catch(setError);
  • We no longer need if(data) before our return. So remove that and the <h1> return currently there. Just return <GithubUser …/> component.
  • Above there return add something to display if we’re loading (like for a super slow API):
    • if(loading) return <h1>Loading…</h1>;
  • If there’s an error we need to display that somehow, so after that put:
    •  if (error) 
    return <pre>{JSON.stringify(error)}</pre>;
  • If we end up with no data, then return null:
    •  if (!data) return null;

The complete App() code is this:

 

function App() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {    
 fetch(
 setLoading(true);
     `https://api.github.com/users/moonhighway`)      
     .then((response) => response.json())      
     .then(data => setData (data)) // shorthand here is .then(setData) 
     .then(() => setLoading(false))
     .catch(setError); 
}, []);
if(loading) return <h1>Loading…</h1>;
if (error) 
    return <pre>{JSON.stringify(error)}</pre>;
if (!data) return null;
return (    
   <GitHubUser 
      name={data.name} 
      location={data.location} 
      avatar={data.avatar_url}
   />  
);
}

06_04 Fetching data with GraphQL

Start to End summary: From reading JSON directly in last lesson segment to utilizing a GraphQL endpoint  and building our query and fetch options directly in our App.js file.

  • Change the GithubUser component to Lift (yes, totally renaming it)
    • Then within Lift
      • Change <p> tag by removing {location} and adding two new things, so:

        <p>{elevationGain} {status}</p>
      • Remove the <img> tag
      • Also remember to pass {elevationGain} and {status} in to the Lift function:

        Lift({ name, elevationGain, status })
  • Change our fetch url to https://snowtooth.moonhighway.com
    • Keep rest of data fetching logic same (we’re using same approach to our three states of setting the loading state, setting the data, setting the error)
    • Inside the return(), instead of returning GithubUser, we’ll iterate over each Lift using map() and it needs to be wrapped in a <div>. So,

      return (
        <div>
          {data.allLifts.map((lift) => (
            <Lift />
          ))}
        </div>
      );
  • Then pass in the properties within <Lift />
<Lift name={lift.name} elevationGain={lift.elevationGain} status={lift.status} />
  • Now we need to build our query as a variable and some options that we’ll pass with our fetch call. Just after the opening imports in App.js
const query = `
   query {
     allLifts {
       name
       elevationGain
       status
     }
   }
`;
  • And then our options (which will contain the query)
const opts = {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({query})
}
   fetch(
      `https://snowtooth.moonhighway.com`, opts
    )
  • And it doesn’t work, but that’s ok. We need to dig deeper to see the returned JSON and where the data is. So console.log(data, “DATA!!!”); after the fetch and if loading, error, !data checks. Inspect it in browser dev tools.
  • To fix add the second “data” to the map function so, {data.data.allLifts.map((lift) => (
    etc.

06_05 Working with render props

  • Remove nearly all the code in App.js. Keep the App.css import. Keep the return within function App() but rewrite a bit so it has
import "./App.css";

function App() {
  return (
    <div>
      <h1>Hello</h1>
    </div>
  );
}

export default App;
  • Now add a const above our App() function that is an array of objects. Each object containing a name and an elevation. So:
const tahoe_peaks = [
  { name: "Freel", elevation: 10891 },
  { name: "Monument", elevation: 10067 },
  { name: "Pyramid", elevation: 9983 },
  { name: "Tallac", elevation: 9735 },
];
  • Now build a function component that takes in the data, renderItem for rendering the item and renderEmpty boolean. The function will be called List (capital “L” suggests this is a component)
function List({data, renderItem, renderEmpty}) {
  return !data.length ? (
    renderEmpty ) : (
    <ul>
      {data.map((item) => (
        <li key={item.name}>
          {renderItem(item)}
        </li>
      ))}
    </ul>
  );
}
  • And then change our return in App() to return a <List />. We need to pass in the three things: data, renderEmpty, renderItem.
   <List 
      data={tahoe_peaks} 
      renderEmpty={<p>This list is empty</p>} 
      renderItem={(item) => (
      <>
        {item.name} - {item.elevation} ft.
      </>
      )}
    />
Skip to toolbar