React hooks, released in February 2019, have huge benefits when implemented in your application. Whether you've used hooks before or are just learning, this post aims to show you just how simple it is to implement them with Stream Chat.

The Basics

Using hooks with Stream Chat is as simple as it sounds. Wherever you would regularly need local state or other React features, such as componentDidMount , is an example of somewhere you could implement hooks and therefore clean up your code.

For a basic overview of the many types of hooks, check out the React Docs. In a nutshell, there are 3 main use cases:

State Hook: useState adds local state to your component. This includes defining a current state variable, a function to update it, and a value to initialize that piece of state with.

adds local state to your component. This includes defining a current state variable, a function to update it, and a value to initialize that piece of state with. Effect Hook: useEffect gives you the power to perform "side effects" such as data fetching, subscriptions, or other DOM manipulation within a functional component.

gives you the power to perform "side effects" such as data fetching, subscriptions, or other DOM manipulation within a functional component. Build Your Own Hooks: By building your own hooks, you can reuse stateful logic between as many components as needed. It's important to use the same naming convention when creating your custom hook by always using the prefix use (such as naming your custom hook useFriendStatus ) so that the React linter plugin is able to detect bugs.

There are a variety of other pre-made hooks you can use which are all laid out within the Hooks API Reference.

Adding The State Hook

To keep it simple, we've just added a bit of functionality to the Demo App we've already provided for you through our demo page found here. This demo chat app is made for users who need to communicate with their customer base. This demo only utilizes local state, so I'd added some lifecycle methods to it for us to refactor later on.

Here is the Codepen we will start with today. As you can see, App is a class component that utilizes local state to determine whether the Button is set to open or closed. This will be an easy refactor to make the component functional with the useState hook!

For now, we will ignore componentDidMount and componentWillUnmount , and just focus on adding useState . For that reason, those are both commented out for the time being.

Original Class Component:

https://gist.github.com/whitneyburton/b8ef53702e7918b5b82907728d846cb9

In the process of moving from a class to functional component, there are a few things you need to do first.

Import what you need from React - because we are using Codepen, we will access useState and useEffect using dot notation (i.e. React.useState ) instead of with an import at the top of the file. In a typical project use case, you could just add the import to the top of the file: import React, { useState, useEffect } from 'react';

and using dot notation (i.e. ) instead of with an import at the top of the file. In a typical project use case, you could just add the import to the top of the file: Change App to be a functional component.

class App extends Component turns into const App = () => You will also need to add const to the beginning of toggleDemo since we will no longer be able to access it using this .

turns into Remove the render() . Don't forget to delete both of the curly braces! 🙂

These are the few steps I always make sure I complete before moving on to the hooks refactor so that they aren't forgotten about later on. Now our component looks like this:

https://gist.github.com/whitneyburton/72154fed23f6017bf0cdd852ae3df542

Step 1: Functional Component

This will break as is because we are still using constructor() / super() / this.state as well as accessing our local state and functions with this . That's our next step - refactor the component to utilize the useState hook.

First, change constructor / super / this.state into a hook: in order to accomplish this, you can start by simply removing the entire constructor, because you will be defining an entirely new variable using useState .

/ / into a hook: in order to accomplish this, you can start by simply removing the entire constructor, because you will be defining an entirely new variable using . After deleting the constructor, use the same key that you used in state as the new variable name. Since we were using open as the key with true as the initial value, and using on onClick on the button to toggle that boolean, here is what the hook will look like: const [open, toggleOpen] = React.useState(true); open is the new variable name toggleOpen is the function to update the value true is the value we want to initialize the variable with, so we pass that into useState

as the key with as the initial value, and using on onClick on the button to toggle that boolean, here is what the hook will look like:

https://gist.github.com/whitneyburton/5667c69376e1e3b9a456c525358fbdc2

Step 2: Functional Component

Our refactor is almost complete. The last step is to update any references to this , this.state , and this.setState to reflect our new functional component structure and state hook. That will change a few areas:

this.state.open is now: open

is now: this.setState({ open: [true or false] }) is now: toggleOpen([true or false])

is now: this.toggleDemo is now: toggleDemo

Here's the final result:

https://gist.github.com/whitneyburton/2cc5037f4938ec4e03f0fbbbcfb1a746

Cleaning It Up

To shorten your code up even more, you could switch the toggleDemo function into a quick ternary conditional since our toggleOpen update is so short:

https://gist.github.com/whitneyburton/341a0642e690e02828164932627feba6

Overview

Overall, this small refactor took our component from 55 lines to 35. Leveraging the useState hook allows us to quickly and easily set and update local state.

Adding The Effect Hook

Now let's look into adding the useEffect hook! This means that we get to comment in our componentDidMount and componentWillUnmount lifecycle methods. For checking in on the functionality of the lifecycle methods, it's best to go back to our original Codepen. Within that you'll notice:

componentDidMount does two things: First, it logs that the component rendered (this is for anyone who's new to React and just wants a reminder of when this fires) Then, it utilizes Stream Chat's sendMessage() method (see the docs on this here) to demonstrate how you can send a pre-populated message to your customers when they join the chat.

does two things: componentWillUnmount simply logs the number of state messages that you have in local state before unmounting the component. This shows you how you can check the number of local messages in your future app, and is generally just here to show you how to run clean up functions with useEffect .

Step 1: Setup The Hook

Refactoring these two lifecycle methods to utilize the Effect Hook is easier than you might think. We will start by hashing out the useEffect method. Within CodePen, as stated above, you'll have to use dot notation to access it. This is what the refactor looks like to start:

https://gist.github.com/whitneyburton/dfcdd7ea631752fc58572c34bda7ac06

Step 2: Refactoring componentDidMount

Whatever is usually put within your componentDidMount can just be plopped right into this function. So, in our example, we take the console.log and channel.sendMessage within useEffect like so:

https://gist.github.com/whitneyburton/1ad020d65989d8c4659d6b91060ee99b

That is all that you need to do to add the same functionality as componentDidMount with a hook! 👏

You can see this functionality in action with this Codepen.

Step 3: Refactoring componentWillUnmount

In order to add in logic which "cleans up" just before your component unmounts, all you have to do is return a function within your useEffect . For example, within our original componentWillUnmount , the only logic we performed was:

https://gist.github.com/whitneyburton/fe247a5c065b5e6b5ef069ebbb60e8a2

In order to add this effect to useEffect , just put that log into a function, and return it at the end of the effect, like so:

https://gist.github.com/whitneyburton/82ab3ef04692d572d6f3f581e8bd29f5

Easy as that! Now we've added all functionality back to our component, and the transition to a functional component with Hooks is complete. Here is the complete Codepen for your reference.

https://gist.github.com/whitneyburton/98b2792f93ab7abf6e60f06532ac53b7

Step 4: Skipping Unnecessary Effects

The only "gotcha" with useEffect is that there can be performance issues. We encourage you to read up on the strategies to optimize performance based on your needs within your application as referenced in the linked docs above. However, in our simple use case, all we need to do is pass in an empty array as a second argument. Essentially, by passing in this empty array, we are telling the Effect to not re-render when props and/or local state changes. This is because in our case we only want our Effect to run and clean up one time.

https://gist.github.com/whitneyburton/904698b418bf24a88ac2a2b980bf7430

Because each click of the Button component triggers the toggleOpen function to change the open bool in state, the entire component re-renders to reflect that. Without the empty array, our useEffect would fire with each click, and therefore log "componentDidMount - rendered!" and, more importantly, re-send our "Welcome to our customer chat with React Hooks tutorial!" message every time. This is easily avoided by adding the empty array. Our welcome message is sent only once despite the chat box being open/closed - as intended. 🙂

Summary

As we all know, the frameworks we work with daily are constantly changing. React is the perfect example of a powerful framework that is consistently coming out with their versions of the latest and greatest tech. Adding Hooks is a simple process and significantly cleans up your code.

The great news is that there are no plans for React to remove classes, so you can keep the classes you are currently using, and just start implementing hooks within the smaller, and therefore more simple, components as you go. As the docs state, they are 100% backwards compatible. These are just the basics of hooks to get your feet wet and demonstrate how seamlessly they integrate with Stream Chat, which makes for an easy and exciting developer experience. 🙌