How and Why to use React Hooks
March 28, 2020
Why Hooks
React Hooks is a newer, opt-in way to write React code as of version 16.8 that comes with various pre-defined hook methods that serve a variety of functions. Hooks can only be used in functional components and have a syntax that is shorter and easier to read than class-based components. Hooks are optional, and can be converted one at a time when convenient.
If you’ve written any React code in the past, there’s a good chance you’ve made a class component before. They are generally straightforward and simple to use. State is stored in an object using this.setState and effects are called using lifecycle methods. These features do not exist in hooks, however they are replaced by similar ones.
The basic hooks we will be using in this tutorial include
- useState
 - useEffect
 - useContext
 
There are also additional hooks such as
- useCallback
 - useRef
 - useReducer
 - useMemo
 - useLayoutEffect
 
Importing Hooks
Hooks come included with React 16.8 (and above), so you will not need to install any additional dependencies to get started. Importing these hooks is a similar to how you import React.Component in class components. For example, you can import the useState and useEffect hooks in your React import as follows
Import React, { useState, useEffect } from 'react';*Alternatively you can use these hooks directly with React.useState() or React.useEffect()
useState
This hook serves the same purpose as our state object in class-based components. A basic useState hook is declared, generally at the top of your functional component’s JavaScript like this
const [data, setData] = useState([]);Here, we are declaring the variable we want to keep track of data (equivalent to this.state.data in classes) as well as a setter function setData (similar to this.setState({ data })). The initial value for data gets passed in as the useState argument (an empty array in this case).
useState vs this.setState
If you’ve used many class-based components before, you’ll know that this.setState only changes the properties you specify. For example
state = {
  user: "jimmysmith@gmail.com",
  data: []
}If we were to call the following code
this.setState({ data: [1,2,3] });our data value in state would be adjusted and the value for user would remain the same. this.setState only overwrites the specified properties.
The useState hook is slightly different in its effect. For example if we had the following hook
const [state, setState] = useState({
  user: "jimmysmith@gmail.com"
  data: []
})Calling the following method
setState({
  data: [1,2,3]
})would overwrite the user value jimmysmith@gmail.com in our state hook.
We can avoid this issue by first spreading our hook’s value, then changing our desired properties
setState({
  ...state,
  data: [1,2,3]
})This will add the user and data properties from state, then replace data with our array.
The useState hook can take some time to get used to, but once you understand how to implement state using this hook, it will end up saving you complexity in your code. You can either declare useState hooks for each variable, or use one hook to store an object with all of your state data.
useEffect
This hook will try to run each time your application re-renders. It takes two arguments: a function to run, along with an optional array that indicates when the hook should trigger.
On Component Mounting
As a basic example, the following useEffect hook will have the same effect as componentDidMount in a class-based component
useEffect(() => {
  console.log('functional component mounted');
}, [])Here the empty array indicates that the effect hook should run on the first render, and not again after that. If we were to leave off the empty array and just provide an arrow function, that function would run on every render.
On Component Updating
Sometimes we will only want to run this effect when one of our components props updates. In a class-based component this would look similar to 
class ClassExample extends React.Component {
  componentDidUpdate(prevProps) {
    if(prevProps.data !== this.props.data) {
      console.log('data prop was updated');
    }
  }
  ...
}Since this prevProps !== this.props logic is regularly used in componentDidUpdate, this code is simplified in the following useEffect hook
const HookExample = ({ data }) {
  useEffect(() => {
    console.log('data prop was updated');
  }, [data])
  ...
}Passing data into the second argument’s array in useEffect indicates that this hook should be run on any re-render in which the data property changes. We can include multiple variables in this array, separated by comma, in order to run the same logic when different variables change.
useContext
Context in React has been around for a while, however useContext simplifies its use in functional components. It allows us to pass down props layered deeply in the component tree, without having to declare them on each individual child component.
To create a context, declare a new variable like this
const DemoContext = React.createContext();Each context has access to  Provider and Consumer wrappers which pass props down your component tree. By wrapping your components in a Provider and supplying a value property, any component inside that Provider will also be able to read that value by accessing the Consumer.
const App = () => {
  return(
    <DemoContext.Provider value={{
      color: 'green',
      number: 42
    }}>
      <Page />
    </DemoContext.Provider>
  )
}
const Page = () => {
  return(
    <Item />
  )
}
const Item = () => {
  return(
    <div>Item here</div>
  )
}In this example, we have an Item component nested three levels deep which doesn’t receieve any props from its parent components. If we wanted, we could pass these values down through the props of the Page and Item components. The context hook instead allows us to pull in those values with the useContext hook.
const Item = () => {
  const demo = useContext(DemoContext)
  return(
    <div style={{color: demo.color}}>Item {demo.number} here</div>
  )
}As long as the DemoContext we created above is imported in the Item component’s file, we will have access to all the properties passed down in the provider. Here the consumer is extracted from the DemoContext and wrapped around the entire component. useContext allows us to cleanly consume parent properties without having to wrap our child component in a consumer and use a render props pattern that is normal for class-based components.
Conclusion
There are lots of other hooks available in React, including custom hooks. This tutorial included some of the most commonly used ones. Overall, there are plenty of benefits and reasons to get writing functional components with Hooks today!
