Introduction to React hooks

Bartłomiej Szajewski

React 16.8 introduced some new functionalities, one of which is _Hooks_. What are these? _Hooks_ are functions provided by React that let us hook into React features from function components.

Introduction

React 16.8 introduced some new functionalities, one of which is Hooks. What are these? Hooks are functions provided by React that let us hook into React features from function components.

Motivation

In the process of React projects development, the developers faces lots of serious issues:

Reusing logic

The most important one is that there is no easy way to reuse stateful logic between components. You might remember mixins from old versions of React which tried to solve this problem but they had more cons than pros so were just removed. Instead, React team introduced two patterns – higher-order components and render props. Unfortunately, these require you to restructure your components, which decreases readability of the file structure, so if you look at DevTools, you may see a wrapper hell.

Hooks allow you to reuse stateful logic without any changes in component hierarchy. You can always extract your stateful logic from component, which means that it might be reused and tested independently!

Huge components

If you’ve ever worked with React projects, you’re surely aware that even simple components end up being more and more complex over time. New requirements add a new stateful logic and side effects. Lifecycle-related methods grow and new methods are introduced. Data is being fetched, methods are subscribed to and event binding is done in componentDidMount. The same with componentWillUnmount, where pending request are cancelled, methods unsubscribed and events unbound, which can cause memory leaks if forgotten.

Sometimes we are not able to split our component into smaller ones because of complex stateful logic which is also hard to test. Hooks solve the issue by letting us divide code into smaller functions based on what pieces are related to, e.g. fetching data, settings up a subscription or event handlers.

Confusing classes

Classes are hard to read for humans, especially due to JavaScript being very different than most other programming languages. It gets even more complicated, because you have to remember to bind all methods that refer to this or use an arrow function within a declaration.

It turns out that the process of class minification is problematic. Transpiler does not know about the visibility of class methods, so it can not minify the name of the methods, because they might be already used somewhere outside of a class.

Hooks solve the problem by allowing the user to use React features without the need to create classes.

Code example

Controlled input implementation based on classes:

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      name: "Adam"
    };
    this.onChange = this.onChange.bind(this);
  }

  onChange(e) {
    this.setState({
      name: e.target.value
    });
  }

  render() {
    return <input value="{this.state.name}" onchange="{this.onChange}">;
  }
}

Controlled input implementation based on hooks:

const App = () => {
  const [name, setName] = useState("Adam");
  const onChange = e => setName(e.target.value);

  return <input value="{name}" onchange="{onChange}">;
};

After transpilation and minification:

Classes:

"use strict";
var _createClass = (function() {
  function e(e, t) {
    for (var n = 0; n < t.length; n++) {
      var r = t[n];
      (r.enumerable = r.enumerable || !1),
        (r.configurable = !0),
        "value" in r && (r.writable = !0),
        Object.defineProperty(e, r.key, r);
    }
  }
  return function(t, n, r) {
    return n && e(t.prototype, n), r && e(t, r), t;
  };
})();
function _classCallCheck(e, t) {
  if (!(e instanceof t))
    throw new TypeError("Cannot call a class as a function");
}
function _possibleConstructorReturn(e, t) {
  if (!e)
    throw new ReferenceError(
      "this hasn't been initialised - super() hasn't been called"
    );
  return !t || ("object" != typeof t && "function" != typeof t) ? e : t;
}
function _inherits(e, t) {
  if ("function" != typeof t && null !== t)
    throw new TypeError(
      "Super expression must either be null or a function, not " + typeof t
    );
  (e.prototype = Object.create(t && t.prototype, {
    constructor: { value: e, enumerable: !1, writable: !0, configurable: !0 }
  })),
    t &&
      (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : (e.__proto__ = t));
}
var App = (function(e) {
  function t(e) {
    _classCallCheck(this, t);
    var n = _possibleConstructorReturn(
      this,
      (t.__proto__ || Object.getPrototypeOf(t)).call(this, e)
    );
    return (n.state = { name: "Adam" }), (n.onChange = n.onChange.bind(n)), n;
  }
  return (
    _inherits(t, React.Component),
    _createClass(t, [
      {
        key: "onChange",
        value: function(e) {
          this.setState({ name: e.target.value });
        }
      },
      {
        key: "render",
        value: function() {
          return React.createElement("input", {
            value: this.state.name,
            onChange: this.onChange
          });
        }
      }
    ]),
    t
  );
})();

Hooks:

"use strict";
var _slicedToArray = (function() {
    return function(r, t) {
      if (Array.isArray(r)) return r;
      if (Symbol.iterator in Object(r))
        return (function(r, t) {
          var e = [],
            n = !0,
            a = !1,
            i = void 0;
          try {
            for (
              var u, o = r[Symbol.iterator]();
              !(n = (u = o.next()).done) &&
              (e.push(u.value), !t || e.length !== t);
              n = !0
            );
          } catch (r) {
            (a = !0), (i = r);
          } finally {
            try {
              !n && o.return && o.return();
            } finally {
              if (a) throw i;
            }
          }
          return e;
        })(r, t);
      throw new TypeError(
        "Invalid attempt to destructure non-iterable instance"
      );
    };
  })(),
  App = function() {
    var r = useState("Adam"),
      t = _slicedToArray(r, 2),
      e = t[0],
      n = t[1];
    return React.createElement("input", {
      value: e,
      onChange: function(r) {
        return n(r.target.value);
      }
    });
  };

So what about classes?

React team has no plans to deprecate classes so there is no rush to migrate to hooks. Both can work side-by-side, but the best idea is to practice the usage of hooks in new components/projects.

Summary

You no longer need to use classes, if you want to create stateful components. The structure and performance of your application can be improved by using Hooks in your functional components. You also no longer need HOCs or render props, if you want reusable components that handle the logic. This can be solved by the mixture of hooks and functional components. So let forget about classes and use hooks instead!

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami