React Hooks were introduced in React version 16.8. Hooks are functions that let you “hook into” the React state and lifecycle features straight from function components. There are many benefits of using react hooks. With help of React Hooks we can use React without classes. It allows us to write more readable, cleaner code with less lines of code.

useState

Let’s look at the simplest class component example with some state:

import React from 'react';

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
    this.handleButtonClick = this.handleButtonClick.bind(this);
  }

  handleButtonClick() {
    this.setState({ count: this.state.count + 1 })
  }

  render() {
    return (
      <button onClick={this.handleButtonClick}>
        {`click: ${this.state.count}`}
      </button>
    );
  }
}

We are keeping the count value in component state. Now the same example using functional components with useState hook. It looks much better:

import React, { useState } from 'react';

function Counter(props) {
  const [count, updateCount] = useState(0);

  function handleButtonClick() {
    updateCount(count + 1);
  }

  return (
    <button onClick={handleButtonClick}>
      {`click: ${count}`}
    </button>
  );
}

The benefits of using functional components are visible at first glance. Less code is required. We do not need to initialize the constructor or bind functions. In this example we are using the useState hook to keep the component state:

const [count, updateCount] = useState(0);

With this hook we made the count a stateful variable. The second returned value is a function to update it: updateCount. We use it each time we will update the count variable: updateCount(newCountValue). Each update will cause re-render of the component. The hook is called with attribute 0 - this is the initial value. Additionally we do not need to call this.state.count, we simply use count.

useEffect

The useEffect hook is the second of the three main React Hooks. It is used to run additional code after render. We can see it as combination of React Component lifecycle methods: componentDidMount, componentDidUpdate, and componentWillUnmount. We can use it for example to add listeners after the component has rendered.

If needed the effect can return a function which is a cleanup function. It is used to perform cleanups e.g. to prevent introduction of memory leaks. It this example we are removing the event listener in the returned cleanup function.


import React, { useEffect } from 'react';

function Position(props) {
  function handleMouseMove(e) {
    console.log(e.clientX, e.clientY);
  }

  useEffect(() => {
    window.addEventListener("mousemove", handleMouseMove);
    return (() => {
      window.removeEventListener("mousemove", handleMouseMove);
    });
  }, []);

  return (<div/>);
}

By default the useEffect hook runs after every render. Sometimes it is not needed to run the ‘effect’ on every change. For example if we want to perform the effect only on certain values change. We can do so by passing an array as an optional second argument to useEffect. In this case the effect will be run after count update only:

useEffect(() => {
  console.log(count);
}, [count]);

In previous example we passed an empty array []. This causes to run the hook to run only on component mount (and cleanup on unmount), which is what we wanted there.

The main benefits of using useEffect hook is encapsulating side effects.

useContext

Last of the three main React Hooks is useContext. It is used to share data across components. It allows us to create a global state that can be used even in deeply nested components without the need to pass local data around. Let’s see how can we use it. First we create context:

const ThemeContext = React.createContext();

As next we create a provider wrapper. The Provider accepts a value prop which is used to pass the context. In our example it’s the theme variable and handleThemeChange function. We will have access to them through the useContext hook.

const ThemeContext = React.createContext();

function ThemeWrapper({ children }) {
  const [theme, changeTheme] = useState("dark");

  function handleThemeChange(theme) {
    changeTheme(theme);
  }

  const context = {
    theme,
    handleThemeChange
  };

  return (
    <ThemeContext.Provider value={context}>
      {children}
    </ThemeContext.Provider>
  );
}

Now we wrap the content with our provider:

function App(props) {
  return (
    <ThemeWrapper>
      <Content />
    </ThemeWrapper>
  );
}

Finally we can connect to the context using the useContext hook:

import React, { useContext } from "react";

function Content(props) {
  const { theme } = useContext(ThemeContext);

  return (<div className={theme}>...</div>);
};

We have an easy access to our context. The context is provided to each component in the component tree and will re-render whenever the Provider’s value prop changes. Using useContext hook we do not need to pass the props all the tree down.

Want more?

comments powered by Disqus