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.
0 comments :
Post a Comment
What do you think?