In this React component tutorial by example, we will create a React Range Component with React Hooks and a Function Component . You can see the final output of this implementation in this CodeSandbox or in this GitHub repository . If you want to implement it step by step, just follow the tutorial.
We are starting off with the previous tutorial where we have implemented a React Slider Component. Letâs rename all its internals from Slider/slider to Range/range to keep our naming of things consistent. This tutorial will extend the component to a Range Slider which has a couple of more features. Letâs get started.
First, we want to colorize the range â or also called track â which is used for our interactive thumb to move from minimum to maximum of the rangeâs capacity. But we will only colorize the part left of the thumb. This way, we get a visual feedback about which range has been selected and which not.
Essentially we are doing four things here to update the range without Reactâs state management:
Next, we are going to introduce a minimum ( min ) value next to our already familiar maximum ( max ) value. This way, we are not always counting from 0 to maximum, but can choose to have two dynamic values (min and max) for our range. If no minimum value is set for our Range component, we will default to zero.
We are showing the minimum value, but we are not using it yet for our calculations of the new value and percentage in our mouse move handler and our initial calculation for the percentage. Before we just assumed in our calculations that our minimum to be zero. Letâs change this by taking the min into account for our value and percentage calculations:
When interacting with the Range componentâs thumb, you will notice that the trackâs progress, the thumbâs position, and the current value are correct â even though the min value isnât zero. The current shown value shouldnât go below the defined min value.
Next, we will do a refactoring for our React Range component. So far, everything is initialized once when our component renders for the first time. We are doing it the declarative way with our JSX â thatâs how React taught us at least how to do it:
However, since we are already using the imperative way to update all of these values once someone moves the range in our component, we could use the imperative way of doing things for the initial rendering as well. Letâs remove the JSX for the initial rendering and use a React Hook instead to trigger the update function imperatively.
First, letâs move everything that needs to be updated to its own function:
Second, letâs remove the declarative JSX and replace it with a React useLayoutEffect Hook that runs with the first rendering of the component (and on every dependency change) to update all displayed values with our previously extracted updater function:
Now we run this React hook on the first render and every time one of its dependencies changes â hence the second array as argument â to handle the update imperatively instead of relying on JSX.
Last, we need to wrap our update function into Reactâs useCallback hook, otherwise the update function would change on every render and run our useLayoutEffect hook again and again. The handleUpdate function should only be re-defined when one of its dependencies (here formatFn ) changes.
The âhandleUpdateâ function makes the dependencies of useLayoutEffect Hook change on every render. To fix this, wrap the âhandleUpdateâ definition into its own useCallback() Hook.
Everything should work again. However, keep in mind that itâs recommended to avoid the imperative way of doing things in React. So see this as an exercise to move things from declarative (JSX) to imperative (useRef) programming â since we needed the imperative programming anyway for updating everything on our mouse move event without using Reactâs state management. In the future, try to stick to Reactâs declarative way of doing things for state management and displaying values.
The React Range Component was inspired by this pure JavaScript implementation . Let me know in the comments how you improved your component and how you liked the tutorial.