1/11/20

React Function Component Monitor State Changes with Context Hooks

When using React components, there are several lifecycle events that enables us to perform some logic on the behavior of the component. For example, we can check when the component was mounted or when a state variable was changed.  In the case of Function Components, there is no support for lifecycle events because these components are stateless.


In some instances, there is a need for a function component to monitor a state change. For those cases, the first thing that comes to mind is to just use a full component instead. However, what if we want to stick with function components?  What if your function components just need to track an application state change and reflect that change? Well for those cases there is a new alternative.

In the latest releases (16.8) of React, we can use Hooks to monitor a state change and render elements dynamically as a result of the state change somewhere in the app. Let us look at that more in detail by learning about Hooks and how they can help.

Hooks

React Hooks were created to resolve several problems across components.  Basically, as state needs to be shared by multiple components, hierarchy dependencies had to be created in the form of render props and high-order component often leading to a somewhat unclear implementation.

With Hooks, we can use stateful logic independently without impacting a component hierarchy. We can track state changes independently in a non-invasive approach to our existent component.

Now that we have some background, let us look at a specific use case and see how Hooks can help us solve the problem. 

Use Case

As a use case, we have a function component that manages an HTML element on the page. When we first build the application, the element was just meant to show a static element. As usual, the application features can change based on user feedback, and we now need to make this element dynamic.  We want to be able to respond to an application state change and render a different behavior. Let us first look at the code to see how this function component looks without any state management.

App Component

 

//index.js

import React, { Component } from 'react';

import { render } from 'react-dom';

import Header from './Header';

import './style.css';

class App extends Component {

  constructor() {

    super();

    this.state = {

      name: 'React Hooks',

      status: 'info'

    };

   

  // simple timer to change state

  const timer1 = setTimeout(()=>{

   clearTimeout(timer1);  

   this.updateStatus();

   }, 2500);

 

  }

 

 updateStatus = () => {   

   const newStatus = this.state.status === "info" 

          ? "success" : "danger";

   this.setState({status:newStatus});

 }

 

  render() {

    return (

      <div>

        <Header name={this.state.name} status={this.state.status}/>

        <section>

          React function components and hooks

        </section>

        <footer>

        <a href="//ozkary.com">Blog Reference ozkary.com</a>

        </footer>

      </div>

    );

  }

}

render(<App />, document.getElementById('root'));


This is the app main component which is associated to the root element. This component imports the header component which handles the rendering of the app title and a status component. Let’s review the code for those components.

Header  and Status Components

 

//header.js

import React from 'react';

import Status from "./Status"

export default ({ name, status }) => 

<header>

<h1>Ozkary -  {name}</h1>

<Status status={status}/>

</header>

 

 

//status.js

import React from 'react';

export default ({status}) => {

return (

<label className={status}> {status}

</label>

);

}

 

 

In addition to rendering the application title, the header component also imports a status component which is responsible for rendering a color-coded status indicator. By looking at the component, we cannot assert how this state changes. This however is not that relevant since for us, we just need to track when the state changes.  

What we should notice is how the status property has been passed from the top container (app) down to the header and status components. This can become unmanageable when we are dealing with multiple levels of components. For a possible better approach, we can look at how Hooks can help us resolve this without using props.

Lifecycle Hooks

For the purpose of this writing, we are looking at the useContext Hook which enables us to use lifecycle methods in a functional component. This is what we need to be able to track a state change and render the new behavior.

For this approach, we first need to use the  React Context API which enables us to create context variables that can be used in other areas of the app using Hooks.  For this to work, we need to create a context provider which allows us to provide the data to other components by wrapping them into a provider markup.  

Context Provider

 

// context.js

import React from 'react'

 

const StatusContext = React.createContext("");

 

// provides the contexr to other components

export const StatusProvider = StatusContext.Provider

 

// use context to hook into the changes

export default StatusContext

 

We are now ready to remove our props dependency from the header and status components, and instead, we can add the status context provider and required mark-up. After updating our code, we can focus only on the areas of change to illustrate how we are importing a new file and adding the StatusProvider tag around the Header component.

Revised App Component

 

 

import {StatusProvider} from "./context";

 

class App extends Component {

 

  render() {

    return (

      <div>

        <StatusProvider value={this.state.status}>

          <Header name={this.state.name}/>

        </StatusProvider>        

        <section>

          React function components and hooks

        </section>

        <footer>

        <a href="//ozkary.com">Blog Reference ozkary.com</a>

        </footer>

      </div>

    );

  }

}

 

On the Header and Status component, we have removed the props dependencies and use the useContext Hook to listen to state changes outside the function component.

 

// header.js

export default ({ name, status }) => 

<header>

<h1>Ozkary -  {name}</h1>

<Status/>

</header>

 

// status.js

import React, { useContext } from 'react';

import StatusContext from "./context"

 

export default () => {

   const contextStatus = useContext(StatusContext);

 

return (

<label className={contextStatus}> {contextStatus}

</label>

);

}

 

With our revision, we are no longer nesting all these props downstream to all other components. There is however the limitation that we need to introduce the provider element into the view to allow us to hook the context changes downstream to the other functional components. 

Conclusion

When we find ourselves having to pass down the props to multiple levels of stateless components, we may want to look at using context and Hooks to prevent this approach and just receive data change events.

Thanks for reading.

Originally published by ozkary.com