Product Engineering

React Hooks – States, Effects And A Cleaner Code

React_Hooks

Overview

Facebook’s official React.js conference, “React Conf 2018” was recently held at Henderson, Nevada on October 25-26, 2018. During this conference, the React team made a major announcement on React Hooks. React Hooks was introduced as a new way to write React components, instead of using React Classes. Though the approach is still in “proposal” stage and under discussion as an open RFC, the sneak preview demonstrated in two sessions articulated its objectives clearly. An initial alpha implementation is already available in version 16.7.0-alpha and we had an opportunity to experiment with it. This blog summarizes our understandings about React Hooks.

 

Need a mind-shift change for React Hooks

As a part of this code-rich blog, we have built a React component using Hooks, to demonstrate three major changes:

  1. useState instead of state variables
  2. useEffects instead of lifecycle events like componentDidMount
  3. useReducer for state transitions

While speaking about “React Today and Tomorrow”, Dan Abramov mentioned that this new approach will require a mind-shift change. The React team has gone out of the way to convey that the earlier mechanism of “classes” will continue, however also mentioned that “Hooks” is their vision of the future. While the community was cautioned to avoid rewriting the old code with a new approach of Hooks, Ryan Florence’s session named “90% cleaner React” was convincing enough about the benefits of React Hooks. Readers are urged to look at the videos of their sessions on React Hooks for the complete picture.

While building this component using React Hooks, we also realized that initially, our classic thought process of writing callbacks in class functions needs a bit of readjustment. It took some time to understand the way useState variables reflect the changes after some delay. That’s why, the current code, at places, uses the pre-useState values despite calling the setter function.

 

Component Functionality

The component developed using React Hooks is a cut-down version of HTML audio tag to play music. Outwardly, its behavior is similar to <audio> e.g. ability to start/pause an audio file. Under the hood, it uses AudioContext APIs so that, it will be possible to extend the audio pipeline using AudioNodes and effects such as Gain, Filters, Pitch adjustment, Tempo adjustment etc.

Even though the <audio> tag is feature-rich, this limited functionality was chosen to demonstrate how React Hooks can lead to a cleaner code in such an asynchronous situation. For now, it has simple features of play, pause and seek.

React Hooks

 

Using the component

For the purpose of the blog, this component can be implemented in standalone mode and it is used as follows. Be aware that this code is specific to React 16.7.0-alpha and Babel

<script crossorigin
src='https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js'>
</script>
<script crossorigin
src='https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js'>
</script>
<script crossorigin
src='https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js' charset='utf-8'>
</script>
<script type='text/babel' src='musicplayer.js'></script>
<link rel='stylesheet' href='musicplayer.css'></script>
<div id='song1'></div>
<script type='text/babel'>
       var player1 = <MusicPlayer src='a2002011001-e02.wav' />;
       ReactDOM.render( player1 , song1 ) ;
</script

Therefore, from the standpoint of usage, there is no difference and is compatible with the way you would use React.

 

Implementing the component

1. Rendering user interface

The React component is implemented as a function instead of class. It still takes in the same “props” argument just as the render function. To that extent, implementing a function component feels as if the logic spread around in different methods of the class is now collated in a single function. There are pros and cons that have been addressed in the React Conf session. The use of “refs” is unchanged, however, their definition mechanism has changed (more on that later).

function MusicPlayer( props ) {
…
…
return(
              <div className='player'>
                     <button ref={playPauseRef} onClick={onPlayPause}> Play </button>
                     <div ref={positionRef}> </div>
                     <input ref={sliderRef}
type="range"
min="0"
max="100"
className="slider"
value={sliderValue} onChange={onSliderChanged} />
              </div>
       ) ;

 

2. Handling callbacks via useEffects

React developers familiar with componentDidMount and componentDidUpdate will take a while to habituate themselves to this new functionality in React Hooks. It has certain similarities but is more powerful. UseEffects is a function, which takes in a function as a parameter, as shown below. The second parameter is important. When passed an empty array, the function is called once at the time of componentDidMount. When passed a set of properties, the corresponding function is called, just like componentDidUpdate.

useEffect( () => {
       loadSound(props.src) ;
},[]) ;
useEffect( ()=>{
       showPosition() ;
}, [offset,duration,tick]) ;

In the code snippet above, first ‘useEffect’ is called once the implementation loads the sound file using an AJAX call. Second ‘useEffect’ is called every time there is a change to the three properties offset, duration and tick.

 

3. Track state changes (useReducer)

This is a new concept and will take more time to grasp. It is a single API to make a change to the state and get back the changed state. Similar to useState, this call also returns a function to be invoked for making changes to the state variables. At first glance, it feels like useState(), but with an ability to influence the new value and perform additional processing, as in the code below.

const [state, dispatch] = useReducer( (state,action)=> {
       switch(action) {
              case 'start' :
                     startPlay() ;
                     return 'started' ;
              default :
                     stopPlay() ;
                     return 'stopped' ;
              }
       },"stopped");
//This function is triggered on click of play / stop button in UI
const onPlayPause = (ev) => {
       if ( ! loaded )
              return ;
       //Call appropriate dispatch
       if ( playing )
              dispatch("stop") ;
       else
              dispatch("start") ;
       } ;

For our component, the same button starts or pauses the sound, via ‘onPlayPause’. In turn, it invokes the dispatch function of the useReducer. Depending on current value and action, reducer dispatch function returns the new state value. Once again, like the useState function, useReducer can be used to set an initial value.

 

Summary

In a nutshell, the new approach of React Hooks looks promising and delivers the true motive of a cleaner code despite the need to think differently. There are, of course, some nuances and we encourage the readers to pick up the full code from the GitHub account.

As next step, we aim to build more components and come up with a plan of action to help our customers with their React implementations, many of whom, are already using React framework for business applications.