DEV Community

BitPunchZ
BitPunchZ

Posted on • Updated on

React Hooks in a Nutshell (Incl Data Fetching, Custom Hooks, Context and Usages)

So this is my attempt to give people who are new to hooks a quick overview of the most important hooks that you should know alongside basic examples of usage of each one to get you started.

Now off we go .

Alt Text

General intro

There are multiple (seemingly unrelated) problems that hooks attempt to tackle, but for the sake of keeping this as short as possible, you should know that hooks allows you to:

  • Have state in your functional component
  • Reuse a piece of state logic in multiple components
  • Simplify your logic when it gets too big

Let's start with the first hook

useState

useState is the hooks way to have state in your functional component, so for example say you're implementing a clap counter, you'd normally achieve this by implementing a class based components like this:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      claps: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.claps} times</p>
        <button onClick={() => this.setState({ claps: this.state.claps + 1 })}>
          Clap
        </button>
      </div>
    );
  }
}

well, in hooks you can do the same writing this:

function Example() {
  const [claps, setClaps] = useState(0);

  return (
    <div>
      <p>You clapped {claps} times</p>
      <button onClick={() => setClaps(claps + 1)}>
        Clap
      </button>
    </div>
  );
}

And voila, you got a fully functional state (pun intended) .
That const [claps, setClaps] = useState(0); line utilizes array destructuring, which was introduced in ES6

claps is our state field, and it will have an initial value of whatever we pass to useState, which is in that case was 0, so initially claps will be equal to 0

setClaps is the function that we will use to modify that claps state field, as you can see happen when we click on our clap button, this will fire the onClick and the setClaps will be called with the existing value of claps plus 1, which will be the claps field new value

And that covers the first hook !

Alt Text

useEffect

The useEffect hook can be used to emulate alot of the existing lifecycle methods like componentDidMount, componentWillUnmount and componentDidUpdate (and some of the older lifecycle methods ofocourse like componentWillRecieveProps)

But before we see usage example, you should know that useEffect take in 2 params, a function and an array.

The array (let's call it dependencies array) will consist of the names of the values that, when changed, will have the function - the first param - called

what if we didn't have a dependency array?

  • that means that the useEffect function -it's first param- will run on every render, which is usually not what we want

In a later example we'll mention what happen if the dependency array is empty

React to state/props change

So let's see an example

Say you want to do something every time the user claps, for the sake of an example, say you want to console.log a "You clapped" message, with the useEffect hook we can do that this way

function Example() {
  const [claps, setClaps] = useState(0);

  useEffect(()=>{
     console.log("You clapped");
  },[claps])

  return (
    <div>
      <p>You clapped {claps} times</p>
      <button onClick={() => setClaps(claps + 1)}>
        Clap
      </button>
    </div>
  );
}

So what happens here is that every time a state field changes, react checks all of our useEffect (yes there can be a multiple of them in our code, just like there can be multiple state fields defined using useState) and it fires all the functions in the useEffect that, in their dependency array, have the field that got changed

So in our case, when we click on our clap button, the setClaps function is called, the claps state field gets changed, which leads to this useEffect first param (it's function) to be called since it's dependency array contains the claps field

    ...
    useEffect(()=>{
       console.log("You clapped");
    },[claps])

    ...

So this was basically how to emulate componentDidUpdate

This structure can of course also be used to call your useEffect hook function when a prop change, simply by adding whatever props you want to be taken into consideration to the dependency array, so if claps are gotten from the component's parent for example:

function({claps}){
    useEffect(()=>{
       console.log("You clapped");
    },[claps])
    ...
}

Also, since it's called a dependency array, remember that you can have multiple values in it, and the function will get fired if one or more of the values in the dependency array get changed

Data fetching

useEffect can also be used to fetch data, but the key question to ask before using this hook to fetch data is :

what if the dependency array was empty?

  • that means the hooks won't run on every render since we explicitly told it to not watch over any variables, so it will only run on mounting

Which is usually what we want when we want to fetch data

Alt Text

So now that we know how to make the useEffect only run on mounting (emulating the componentDidMount lifecycle), fetching data is as simply as doing this:

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

      useEffect(()=>{
       const data = await axios('https://datafetchingexample/data');
       setData(data);
      },[])

      ...
    }

Cleaning up

Alt Text

Next up is figuring out how to use useEffect to do any clean up that we want to do in our component

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

      useEffect(()=>{
        const source = axios.CancelToken.source();

        const data = await axios('https://datafetchingexample/data');
        setData(data);

        return () => {
          source.cancel();
        };

      },[])


      ...
    }

So as you might've noticed, we added a return in our hook, this return function will run when the component unmounts, making it the perfect place to do any cleaning up (closing a socket, unsubscribing, cancelling a request, etc...basically same usage as componentWillUnMount)

useContext

Next up, using context with hooks

Alt Text

Context is, as you may know, react's way to manage state across components, basically it's react's own redux in a way

It's used when you have some data in a component that you want descendants of that components (direct children or indirect descendants in general) to have access to, so for example say we have a component that fetches data and you want to pass that data to your child, obvious way to do that is using props, but if you want to have that data in your grand grand grand grand child...that's where using props can turn into more of a hassle and where using context makes more sense.

For the sake of explanation however, let's say you want to pass this data to your direct child

So first, we'll create a context that has a value of an empty object
const DataContext = React.createContext({});
Next you should wrap the component you want to pass the context to in
<DataContext value=somevalue></DataContext>
Which is what we did to our child component, all we have to do this is just determine the value of the context through the value property
(in that case we want to pass the data field), so we did

    ...

    const DataContext = React.createContext({});

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

      useEffect(()=>{
        const source = axios.CancelToken.source();

        const data = await axios('https://datafetchingexample/data');
        setData(data);

        return () => {
          source.cancel();
        };

      },[])


      return (

         <DataContext value={{data}}>
           <Child/>
         </DataContext>

      )
    }

Now moving on to our child component, all we have to do is use the useContext hook, pass the context object that we want to it, and simply get data that we added in the value attribute

    ...


    function Child(){
      const {data} = useContext(DataContext);


      return (

         <ul>
           data.map(v=>{
             return (
               <li>
                 {v.value}
               </li>
             )
           })
         </ul>

      )
    }

So now that we've covered arguably the 3 most popular hooks, let's talk about the general rules of hooks

Hooks Rules

Only call hooks at the top level

This means that you should not use hooks inside loops,if conditions or nested functions, you should always use hooks at the top level of your react function, that's because hooks depend on the order that they are initialized in, so if you for example add a hooks inside a hook in an if condition, that if condition might not be happening in the next render, leading to a disorder in the hooks, we'll talk more about this in another article

Don't call hooks in javascript functions

You can call hooks from 2 places

  • react functional components
  • custom hooks, which we will talk about next

Custom hooks

Alt Text

Now for the last and the core piece of react hooks, making your own custom hooks.

Custom hooks are javascript functions whose name start with use and it has the ability to call other hooks (custom or built in)

Building a custom hook means that you get to extract a piece of logic for you to use it in multiple places, for example, say you got a couple of inputs that accepts name and age


function InputForm(){
  const [name,setName] = useState("")
  const [age,setAge] = useState(0)

  return (
    <div>
       <input type="text" placeholder="Enter your name" value={name} onChange={(e)=>setName(e.target.value)/>
       <input type="number" placeholder="Enter your age" value={age} onChange={(e)=>setAge(e.target.value)}/>

    </div>

  )

}

Now, basically all inputs in our app will have a similar structure, input field with value,onChange attributes, and not only in this file, using state to handle input can in multiple files that you have, custom hooks will let us extract that piece of reusable logic to use it elsewhere

it will look something like this :


function useFormInput(initialValue){
  const [value,setValue] = useState(initialValue);
  function onChange(e){
    setValue(e.target.value);
  }
  return {value,onChange};

}

function InputForm(){
  const name = useFormInput("")
  const age = useFormInput(0)

  return (
    <div>
       <input type="text" placeholder="Enter your name" {...name} />
       <input type="number" placeholder="Enter your age" {...age} />
    </div>

  )

}

Cleaner isn't it? this will have the same affect as the regular way of doing things but now you have a reusable hook that let you have inputs with their changing functionality anywhere in the app, just use the hooks, destructure the value returned in your input tags, and you're good to go !

If you think you need to practice more on the subject while building 4 projects, I encourage you to take a look at this course :

https://www.udemy.com/course/react-hooks-projects-course/

Top comments (4)

Collapse
 
devhammed profile image
Hammed Oyedele

Nice article 👍
I am already using Hooks in production 🎉🎉🎉
And also thanks for the form input helper, this is the new concept I learned.

Collapse
 
rawaneltobgy profile image
Rawan Eltobgy

Great article with a clear explanation! Thanks for that :D

Collapse
 
tomastley profile image
T(h)om

One of the best explanations I’ve seen! Comparing the hook with lifecycles is super helpful.

Collapse
 
acjunior profile image
André Carlos

amazing explaination and animes too 😂