Discover React Hooks

Discover React Hooks

Discover React Hooks

During the ReactConf 2018 the Hooks were presented and to be honest it has shaken up the React community. If you haven’t seen “React Today and Tomorrow and 90% Cleaner React With Hooks” video yet, then I encourage you to do so.

Introducing stateless functional components in React 0.14 allowed us to create a smaller reusable blocks of code. But if this component had to handle some more advanced logic, then we had to convert it into a class. Now, by introducing React Hooks, we’re able to inject some logic into functional components and futhermore we can reuse this logic blocks in multiple components.

What are React Hooks?

React Hooks are functions that allow to “power up” your functional components by adding state or allowing them to cause side effects like fetching data from api (and many more!).

Soon they will be able to recreate all functionality of lifecycle methods in functional components, in short — they will replace class components in most cases.

Right now, not all methods are available as Hooks: componentDidCatchand getSnapshotBeforeUpdate are still missing, but React team says that they are working on it, so we should expect them in near future.

It’s worth to get to know Hooks since:

In the longer term, we expect Hooks to be the primary way people write React components — Hooks FAQ

Why Hooks were created?

In React there was no easy way of reusing only component logic, component inheritance is strongly discouraged. We can use component composition, practices like render props and higher order components but those are not perfect. They can make code really hard to read and debug due to “wrapper hell”.

Usually, components start as small and overtime they getting bigger and bigger, complex ones are hard to follow, understand, debug and test. Hooks let you isolate component logic into smaller functions responsible only for certain pieces (eg. making requests).

The amount of boilerplate needed when you want to use lifecycle methods is also significantly reduced:

class Counter extends React.Component {
  constructor(props) {
    super(props)
    this.state = {counter: 0}
    this.setCounter.bind(this)
  }

  setCounter(counter) {
    this.setState({counter})
  }
  render() {
    return <div>
      Current count: {this.state.counter}
      <br/>
      <button onClick={() => this.setCounter(this.state.counter+1)}>increase</button>
      <br/>
      <button onClick={() => this.setCounter(this.state.counter-1)}>decrease</button>
    </div>
  }
}

vs

function Counter() {
  const [count, setCount] = React.useState(0)
  return <div>
    Current count: {count}
    <br/>
    <button onClick={() => setCount(count+1)}>increase</button>
    <br/>
    <button onClick={() => setCount(count-1)}>decrease</button>
  </div>
}

And, since we are working in one function scope, binding to this is no longer needed. Hooks allow us to reuse our component logic and create solutions for complicated problems in easy way.

But what if I like class components and for some cases they are the best – you may say. Fear not class components still will be there and all your knowledge about React concepts still will be valid, Hooks are just another method to combine them.

Default Hooks

Lets take a look on the most basic Hooks provided with React.

useState

This is the most straightforward hook, it allows us to keep information in components state. Unlike this.state it does not have to be an object and we can use primitive type:

function Counter() {
  const [count, setCount] = React.useState(0)
  return <div>
    Current count: {count}
    <br/>
    <button onClick={() => setCount(count+1)}>increase</button>
    <br/>
    <button onClick={() => setCount(count-1)}>decrease</button>
  </div>
}

It returns an array with two elements, state, and function used to update state. On initial render, it returns a state equal to the value of the first passed parameter.

You can try this one on CodePen.

useEffect

The second basic hook is useEffect, since functional components are executed on each render they shouldn’t cause any side effects like mutations, making API calls, or subscriptions, this hook does address this shortcoming.

By default, the function passed to useEffect is fired after each render. There’s also a way to pass the cleanup function. You only need to return it.

useEffect(() => {
  props.chatAPI.subscribe();
  return () => props.chatAPI.unsubscribe()
})

 

It’s worth remembering that the previous effect is cleaned up before executing the next effect, so we should think about some way of execution limitation to avoid firing these functions on each update. We can do this by adding some conditions.

useEffect(() => {
  props.chatAPI.subscribe();
  return () => props.chatAPI.unsubscribe()
}, [props.chatAPI])

 

If we pass as a second parameter array of variables, the effect will be fired if one of them changes, passing an empty array [] will cause a firing effect on the first mount and before unmounting.

useContext

Context API uses render prop to provide context to components and usage looks more like this:

function Example () {
  return (<Context.Consumer>
  {context => <span>{context}</span>}
  </Context.Consumer>);
}

 

But if we will use Hook, the code is much simpler:

function Example () {
  const context = useContext(Context);
  return (<span>{context}</span>);
}

 

Demo application

I’ve created a simple note-taking application. You can check the demo here and the source code here.

I think that the most interesting part of it is the usage of useReducer Hook. This allows us to not use redux in smaller apps (and maybe even in middle-sized ones).

Let’s take a look at useReducer what is used in the demo application:

const [state, dispatch] = useReducer(function(state, action) {
  switch (action.type) {
    case 'NOTE_ADD':
      return [...state, Object.assign({}, { title: 'New Note', data: '' })];
    case 'NOTE_SAVE':
      state[action.index] = action.note;
      return [...state];
    case 'NOTE_DELETE':
      state.splice(action.index, 1);
      return [...state];
    default:
      return state;
  }
}, []])

 

As you can see, the passed function is a pure function and looks like an ordinary reducer from redux. The second parameter is a initial state.

This hook returns an array with the current state at the first position and dispatchfunction as the second. Now we can change state by dispatching actions eg. dispatch('NOTE_ADD') will create a new note.

And all of it is done without using any additional library like redux.

Summary

Although Hooks look great (and I’m excited to use them in production), they come with some tradeoffs.

You have to call Hooks at the top level of the render function – this means no conditional Hooks because of the order in which they are called matters.

function Example (props) {
  if (props.shouldUseEffect) {
    useEffect(() => {         // Wrong!!!
      console.log('test!')
    })
  }
  return (<span>example</span>);
}

 

Hooks also can only be used in functional components and other hook functions! But they can be mixed, you can put functional component inside the class component and vice versa.

Overall I think that introducing Hooks into React is a step in the right direction. It probably take some time to establish some good practices and guidelines but some libraries already introduced Hooks integrations eg. react-i18next.

Links

Do you like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.

See also

Download e-book:

Scalac Case Study Book

Download now

Authors

Tomasz Lewiński
Tomasz Lewiński

I'm JavaScript and frontend developer, after some time as Full-Stack I decided to focus more on the front part of projects because it's what I enjoy the most.

Latest Blogposts

28.03.2024 / By  Matylda Kamińska

Scalendar April 2024

scala conferences april 2024

Event-driven Newsletter Another month full of packed events, not only around Scala conferences in April 2024 but also Frontend Development, and Software Architecture—all set to give you a treasure trove of learning and networking opportunities. There’re online and real-world events that you can join in order to meet colleagues and experts from all over the […]

14.03.2024 / By  Dawid Jóźwiak

Implementing cloud VPN solution using AWS, Linux and WireGuard

Implementing cloud VPN solution using AWS, Linux and WireGuard

What is a VPN, and why is it important? A Virtual Private Network, or VPN in short, is a tunnel which handles all the internet data sent and received between Point A (typically an end-user) and Point B (application, server, or another end-user). This is done with security and privacy in mind, because it effectively […]

07.03.2024 / By  Bartosz Puszczyk

Building application with AI: from concept to prototype

Blogpost About Building an application with the power of AI.

Introduction – Artificial Intelligence in Application Development When a few years ago the technological world was taken over by the blockchain trend, I must admit that I didn’t hop on that train. I couldn’t see the real value that this technology could bring to someone designing application interfaces. However, when the general public got to […]

software product development

Need a successful project?

Estimate project