DEV Community

Cover image for React hooks introduction
Steven Gutierrez
Steven Gutierrez

Posted on

React hooks introduction

Life before hooks was messy, confusing at times and full of repetitive code. With the implementation of React Hooks in early 2019, React v16.8.0 made the library simpler and more efficient. React soon became the most popular client-side framework/library for front-end development.

What are hooks and how did they impact React?

React hooks are special functions that let developers "hook into" React state and lifecycle features from functional components. Hooks allow you to reuse stateful logic without changing your component hierarchy. Hooks help to break down components into smaller functions based on their logic and relation. Hooks thus reduce the need for classes while letting you use more of React's features. They don't require you to learn complex functional or relative programming techniques making react a practical library.

Previously React relied on class components to handle state, and without a simpler stateful primitive other than class components, React relied on creating a good amount of boiler plate to define the components. As a result code became cluttered, debugging became harder and logic becoming confusing. When trying to breakdown larger components proved difficult, as component logic became scattered, many felt that React had too much of a learning curve. More issues arose as learning how this works in JavaScript, having to remember to bind event handlers and struggling when to use classes over functions continued to deter developers from using the library. However hooks changed things for the better.


When should hooks be used?

Hooks cover all existing use cases for classes as they provide a simpler implementation of state and lifecycle uses.

Displaying a stateful variable like a username, went from a cluttered logical mess, to a more concise component.

With a simple React class component there is a significant amount of boilerplate needed for the code to run:

import React from 'react'

export default class Example extends React.Component{
    constructor(props) {
        super(props) ;
        this.state = { 
            username: '',
        }
    this.handleChange = this.handleChange.bind(this);
  }

    handleChange(e) { 
        this.setState({
            username: e.target.value
        });
    }

    render(){
        return (
            <>
                <h1>Welcome, {this.state.username}</h1>
                <input 
                value={this.state.username}
                onChange={this.handleChange}
                placeholder="Enter Username">
                </input>
            </>
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

Understanding how this works is essential, and the logic could quickly become confusing.

Using React hooks however makes the code quick, simple and allows for an easier understanding of how React state works:

import React, {useState} from 'react'

function App() {
const [username, setUsername] = useState("")

function handleChange(e){setUsername(e.target.value)}
  return (
    <>
      <h1>Welcome, {username}</h1>
      <input 
       value={username}
       onChange={handleChange}
       placeholder="Enter Username"></input>
    </>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

Hook limitations: Classes allow for more customizable components, when compared to components using hooks. However, the React team thought of this and provided us with a method to create custom hooks.


Main Hooks:

1. useState:

Like any other React hook useState must be imported from React at the top of the file:

import {useState} from 'react';
Enter fullscreen mode Exit fullscreen mode

Then called at the top level of your component declaring the state variable. When declaring state, specific syntax [ and ] is required to destructure the array as useState returns two values in an array:

function Component (){
    const [] = useState();
}
Enter fullscreen mode Exit fullscreen mode

The first value is a state variable which, during the first render, will match the initial state that has been passed.
The second is the setter function that lets you update the state to a different value and trigger a re-render:

Note: it is common practice to put set and then the name of the state variable in camelCase when naming variables.

    const [age, setAge] = useState()
Enter fullscreen mode Exit fullscreen mode

A value is then needed for the initial state, this value can be any value type with a special case for functions:

    const [name, setName] = useState('Henry')
Enter fullscreen mode Exit fullscreen mode

In order to use the setter function and change the current state of the state variable we must call the set function with some next state:

function handleClick(){
    setName('George')
}
Enter fullscreen mode Exit fullscreen mode

Note: when calling the set function the current state does not change the already executing code but rather changes what will be returned on the next render.

When everything is all put together it looks something like this:

import {useState} from 'react' 

function Component(){
    const [age, setAge] = useState(28);
    const [name, setName] = useState(Sam); 

    function handleClick(){
        setAge(22);
        setName('George');
    }
//...
}
Enter fullscreen mode Exit fullscreen mode

2. useEffect:

This React hooks lets you synchronize a component with an external system.

useEffect must be imported and called at the top level of your component to declare an Effect:

import {useEffect} from 'react';

function Component () {
    useEffect()
}
Enter fullscreen mode Exit fullscreen mode

The useEffect hook takes two parameters, the first being a setup function that will handle your effects logic. When the component is first added to the DOM, React will run the setup function and continue running the function with each re-render.

useEffect( ()=>{console.log('hello'},)
Enter fullscreen mode Exit fullscreen mode

The second parameter is the dependency or dependencies put into an array [] that has been declared previously. If no dependency is specified then the effect will continue executing indefinitely. An empty dependency can be use to execute the effect once on the first render:

useEffect( ()=>{console.log('hello')} , []) 
Enter fullscreen mode Exit fullscreen mode

Note: Ensure that when thinking of using the useEffect hook you are trying to connect your component to some external system. In order to limit any unnecessary renders make sure your objects or other dependencies are necessary.

When everything is put together it looks something like this:

import {useEffect, useState} from 'react';

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

    useEffect(()=>{ 
        fetch('https://localhost:3000')
        .then(r => r.json)
        .then( (newData) => setData(newData))

 },[])
}
Enter fullscreen mode Exit fullscreen mode

3. useContext:

This React hook lets you read and subscribe to context from your component.
It needs to be imported at the top of the file and called at the top level of the component:

import {useContext} from 'react'

function Component (){
    useContext();
}
Enter fullscreen mode Exit fullscreen mode

useContext takes one parameter, the context that has been previously created with createContext at a previous component higher up on the component tree:

const page = useContext(pageContext) 
Enter fullscreen mode Exit fullscreen mode

The returned value will be the context value for the calling component, this value is determined as the closest corresponding Context.provider above in the tree. When everything is put together it looks something like this:

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
};

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  return (
    <section className='panel'}>
      <h1>{title}</h1>
      {children}
    </section>
  )
};

function Button({ children }) {
  const theme = useContext(ThemeContext);
  return (
    <button className='button'>
      {children}
    </button>
  );
};

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
};

export default App
Enter fullscreen mode Exit fullscreen mode

For a more in-depth breakdown of these and all other hooks visit the React docs reference page for hooks.

Top comments (0)