text: Let's learn React Hooks and Context API by building a Recipe Search App
  Ola John Ajiboye
  Updated on
  ・10 min read
  This is a beginner tutorial in understanding
  React Hooks and the
  Context API. In reality, this is more of an attempt to explain these concepts to my self and to interested readers. This will be in two parts with this first part focusing on the basics of
  hooks. The other part will hopefully focus more on more advanced use cases and the
  Context API. We'll implement the search functionality and convert to using the
  Context API for managing states and avoiding
  props drilling.
  Update : Part 2 is live here
  How are we going to learn?
  We'll look at some contrast on handling the same state logic with a class component, and then with
  React Hooks. We will be building a Recipe Search application to solidify the concepts, we'll then have a bottle of red wine to celebrate😃. I believe messing with the code is the best way to learn.
  Final Version of what we'll be building
  We'll build a simple clone of the food2fork website. This will not be a fully fledged version of it, but we'll use their API with the top 30 recipes. Add some UI logic of our own just to mess with the states and see how we handle them with
  hooks.
  Prerequisite
  - Basic Knowledge of React
  - Javascript ES6 fundamentals [Array methods, Destructuring, etc]
  So what is a React Hook?
  First things first, so what is React Hook? From documentation
  What is a Hook? A Hook is a special function that lets you “hook” into React features. For example, useState is a Hook that lets you add React state to function components.
  In simple English, hooks allow us to "hook" into specific React Features. For example
  useState as the name implies helps us to use state feature in React where we otherwise wouldn't be able to use it, i.e inside a function component. We'll explain a little bit in details the syntax usage etc through building the recipe search application.
  Set Up
  We'll be using create-react-app to bootstrap the application. I have created a repo with a barebone create react app that we can quickly get started with. Simply clone to follow along. Run the command below:
  git clone
  cd Blog-React-Hook-Tutorial
  Going back to the app we'll be building. We shall be using the food2fork API to fetch and search a list of 30 top recipes. However, the API has a limited query we can make per day. I have created the exact JSON response from them for this tutorial. This will be served from here here so that we won't be hitting their server too frequently.
  Let's create the component folder in our
  src folder to hold the different components our app is likely to have. Take a moment to think about that, there will be a component displaying each
  Recipe, a
  RecipeListcomponent to render a list of the recipes, a
  RecipeSearch component and
  RecipeDetails component for displaying additional details for each
  Recipe. All components will be functional components, since using hook is the point of this tutorial.
  If you did everything correctly, you should have a folder structure like below. You can also clone the
  second branch in the repo here to jump to this point in the tutorial.
  If you have not already, one extension I will recommend installing is the
  ES7 React/Redux/GraphQL/React-Native snippets. It speeds up your React development by allowing you to type shorthands to get some React Snippet. You can find out more here
  So let's get
  Hooked
  Why even Hooks?
  One of the most common things you will do in any frontend application is fetching and displaying data, and manipulating display for great UX. React is no different. One of the common use cases of state is to store data from an API call. Before
  hooks, if you needed to have a
  state of any type within your app, you absolutely have to use a class component. You'd also need to fetch data asynchronously within the
  componentDidMount lifecycle. For many, this isn't a mighty problem, but the React team believes it gives rise to the tight coupling of component logic. Also in more complex apps, it is hard to reuse stateful logic. Don't take my word for that, just read the motivation for hooks here
  Let's look at how we'll handle fetching data from the REST API I have created in a classical component, before moving to how we are going to handle it with
  hooks
  import React, { Component } from 'react' import RecipeList from './components/RecipeList export default class test extends Component { constructor(props) { super(props) this.state = { apiResponse: [], } } componentDidMount() { fetch(``) .then(data => data.json) .then(apiResponse => this.setState({ apiResponse })) } render() { return ( <div> <RecipeList recipes={this.state.recipes}> </div> ) } }
  Let's take a look at the same code with Hook and Effect followed by an explanation
  import React, { useState, useEffect } from 'react'; import RecipeList from './components/RecipeList function App() { const url = useState(``) const [recipes, setRecipes] = useState([]) const fetchRecipe = async () => { const recipeData = await fetch(url) const { recipes } = await recipeData.json() setRecipes(recipes) } useEffect(() => { fetchRecipe() }) return ( <div className="App"> <RecipeList recipes={recipes}> </div> ); } export default App;
  There are a couple of things that are obvious right away, we have imported useState and useEffect from
  react. These are the APIs exposed to us that enables us to use
  React Hooks. The
  useState Hook accepts the initial state. In the example above we have initialized it into an empty array. We expect to populate the array with data from the API call. This is equivalent to below in our class component.
  this.state = { apiResponse: [], }
  Furthermore, the
  useState returns a pair of values to us. They are the current state and a function that updates it. So we get back
  [currentState, setStateFunction] from the use state. That is why we have written
  const [recipes, setRecipes] = useState([]) in our application. Where
  recipes is an array to hold our recipe data.
  setRecipe is the function that enables us to update the state, this is equivalent to
  this.setState in a class component.
  In case the syntax looks confusing, these are not React specific syntax, but plain ES6 Javascript. This is known as destructuring. Since the
  useState returns a pair of value, we have destructured it into an array. The name we choose for them does not affect their behavior, it is simply good practice to name them as
  [name of your state, set+name of state], hence we have :
  const [recipes, setRecipes] = useState([])
  If you need some refresher or primer on destructuring, I have written a little bit about it here.
  Another Javascript feature we need to take note of to fully understand what is going on here is
  closures. Due to
  Javascript closure, we have access to the destructured variables from the
  useState anywhere within our function. Hence, inside the
  fecthRecipe function and anywhere within the
  component we can use the
  setRecipe or any other variables without having to call it
  this.setRecipe etc.
  For the purpose of this tutorial. A simplified definition of
  closuresis that it gives us access to the outer (enclosing) function's variables—scope chain, and also to their return values.
  You can read more here, here and watch here
  Let's quickly pass the props down our components and get them set up to display the list of Recipe. Since this isn't a
  Hook specific feature. I am going to skip it. You can find the updated repo up to this point here. I have also added styling to speed things up. The updated repo is on the
  third/hook-in-app.js branch in the repo.
  Using the
  useState
  At this point, your
  App.js should look like below, we have simply passed down the recipes array inside our state as a
  recipes props to the
  RecipeList component. Note, I have also added a loading state with the
  useState and set it back to
  false after data is completely fetched. This the first example of using multiple states.
  import React, { useState, useEffect } from 'react'; import RecipeList from './components/RecipeList' import RecipeDetails from './components/RecipeDetails' function App() { const url = `` const [recipes, setRecipes] = useState([]) const [loading, setLoading] = useState(true) const fetchRecipe = async () => { const recipeData = await fetch(url) const { recipes } = await recipeData.json() setRecipes(recipes) setLoading(false) } useEffect(() => { fetchRecipe() }) return ( <div> {loading ? <h1 className="text-center">...loading</h1> : <RecipeList recipes={recipes} />} <RecipeDetails></RecipeDetails> </div> ); } export default App;
  Next, let's move to the
  RecipeList component and see what we have.
  Here we have simply received the
  recipes passed as a
  prop from the parent
  App component and immediately
  destructured it --see line 5. We then mapped over it, pass down each recipe to the
  Recipe component as
  prop. Nothing particularly interesting here.
  import React from 'react' import Recipe from './Recipe' import RecipeSearch from './RecipeSearch' export default function RecipeList({ recipes }) { return ( <> <RecipeSearch></RecipeSearch> <div className="container my-5"> <div className="row"> <div className="col-10-mx-auto col-md-6 text-center text-uppercase mb-3"> <h1 className="text-slaned text-center">Recipe List</h1> </div> </div> <div className="row"> {recipes.map(recipe => { return <Recipe key={recipe.recipe_id} recipe={recipe} /> })} </div> </div> </> ) }
  Now to the fun part.
  Inside the
  Recipe component, I have added some contrived
  states that are simple enough to fully understand. We'll make an attempt to go line by line, trying to understand what we are doing and how we are handling stateful logic with the
  useState hook . You should have the below in your
  Recipe component.
  import React, { useState } from 'react' export default function Recipe({ recipe }) { const { image_url, publisher, title, recipe_id } = recipe const [showInfo, setShowInfo] = useState(false) const [recipeDetails, setRecipeDetails] = useState([]) const { ingredients, social_rank } = recipeDetails const handleShowInfo = async (e) => { const { id } = e.target.dataset const response = await fetch(`{id}`) const { recipe } = await response.json() setRecipeDetails(recipe) setShowInfo(!showInfo) } return ( <> <div className="col-10 mx-auto col-md-6 col-lg-4 my-3"> <div className="card"> <img src={image_url} <h6>{title}</h6> <h6 className="text-warning"> Provided by: {publisher} </h6> </div> <div className="card-footer"> <button type="button" style={{ margin: `13px` }}{social_rank}</button>} {showInfo ? ingredients.map((i, index) => { return <ul key={index} <li className="list-group-item" >{i}</li> </ul> }) : null} </div> </div> </div> </> ) }
  Let's understand the code above. Like previously, we have received the
  recipe as a
  prop from it's parent component which is the
  RecipeList we have then destructured it immediately within the function argument. We then went ahead to further destructure the parts we intend to use from the
  recipe object. I know what the
  recipe object contains because I have tested the API, so this isn's some magic. This is equivalent to below
  export default function Recipe(props) { const recipe = this.props.recipe const { image_url, publisher, title, recipe_id } = recipe }
  Now to some useful state. If you checked the application. You'd realize that when we clicked on the more details button we get the extra information related to that recipe, specifically the
  list of ingredient and the
  social rating. Take a peek at the gif on top for a refresher. So we need some kind of state to handle the desired user interaction.
  Think about it for a moment, we need a way to toggle between if more info should be shown or not. We also need a way to fetch the info for the specific recipe. The desired result will then be stored in some kind of state. Viola, we have identified at least 2 states. Hence in our Application, we have a
  showInfo and a
  recipeDetails state.
  With the information we have, let's use the
  useState Hook to work with this.
  - First, we declare the
  showInfostate and the function to set the showInfo
  setShowInfo(equivalent to
  this.SetState). We set the value to
  false
  - Second, we declare the
  recipeDetailsand the
  setRecipeDetails. We set the value to an empty array
  [].
  That's hopefully simple enough, we have set up the initial states. And prepared for handling state change with
  setShowInfo and
  setRecipeDetails.
  Moving to the
  handleShowInfo function. This is an
  async function that basically helps to fetch data. And also handling state change to show the info or not. Let's take it apart line by line.
  Since we intend to call
  handleShowInfo on click of a button we have access to the event object. Inside the button, we have set the
  recipe_id as a
  data-attribute. This enables us to grab the
  id for the specific recipe. Inside the
  handleShowInfo, we then get hold of the
  id by extracting it out of the
  event.target property. Since we need to get additional info, there is a need to make an
  HTTP request with the
  id. This is what we have done next and awaited the response. We then converted the value to a
  json and store the value in
  const recipe.
  *Note: * You may need to get an API Key from food2fork. The current key will probably be out of limit.
  The
  recipe we get as response is the slotted inside the
  setRecipeDetails to serve as and updater for the
  recipeDetails. At this point, we have simply set the state of
  recipeDetails to the array response variable
  recipe. This is equivalent to
  this.setState{ recipedDetails: recipe }
  Another thing is that we set the value of
  showInfo to the opposite of what it was. This is what creates the toggle effect whenever the button is clicked. This is equivalent to.
  this.setState{ showInfo: !showInfo }
  And that's about it, in the returned
  jsx we conditionally rendered the info based on the state of
  showInfo when the button is clicked. We have additionally mapped over the array of ingredients to display them as the additional info.
  This is the end of this very basic introduction of
  hooks and perhaps an oversimplification. In the next iteration of this series, we shall examine hooks in more details and then learn the
  Context API.
  Hope you enjoyed the introduction. Feedbacks are welcomed. Stay tuned for the next update and see you soon. Thank you!
  trying image
  thepracticaldev.s3.amazonaws.com/i...