Question

Issue with Custom Slider Component: Thumbs Not Moving on onValueChange Event

I have a custom slider component in my React application where I've added two thumbs using the SliderPrimitive from @radix-ui/react-slider. The issue I'm facing is that whenever I add the onValueChange event, the thumbs cannot be moved, although I can see the changed values in the console. Without the onValueChange event, the thumbs move as expected.

Below is the code for my Slider component:

"use client"

import * as React from "react"
import * as SliderPrimitive from "@radix-ui/react-slider"

import { cn } from "@/lib/utils"

const Slider = React.forwardRef(({ className, defaultValue = [0, 1000], max = 1000, step = 1, ...props }, ref) => {
  const [value, setValue] = React.useState(defaultValue);

  const handleChange = (newValue) => {
    setValue(newValue);
  };

  const shouldDisplayRangeTogether = value[1] - value[0] < 100;

  return (
    <div className="relative w-full">
      <SliderPrimitive.Root
        ref={ref}
        className={cn("relative flex w-full touch-none select-none items-center", className)}
        value={value}
        onValueChange={handleChange}
        max={max}
        step={step}
        {...props}>
        <SliderPrimitive.Track
          className="relative h-0.5 w-full grow overflow-hidden rounded-full bg-gray-200 dark:bg-slate-800">
          <SliderPrimitive.Range className="absolute h-full bg-dark-gray dark:bg-slate-50" />
        </SliderPrimitive.Track>
        {value.map((val, index) => (
          <React.Fragment key={index}>
            <SliderPrimitive.Thumb
              className="block h-3 w-3 rounded-full border border-dark-gray bg-white duration-700 hover:bg-green ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-slate-950 focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50 dark:border-slate-50 dark:bg-slate-950 dark:focus-visible:ring-slate-300"
            />
            {!shouldDisplayRangeTogether && (
              <div
                className="absolute -top-8 w-10 text-center text-sm font-medium transform -translate-x-1/2"
                style={{
                  left: `${(val / max) * 95}%`
                }}
              >
                {props.currency}{val}
              </div>
            )}
          </React.Fragment>
        ))}
      </SliderPrimitive.Root>
      {shouldDisplayRangeTogether && (
        <div
          className="absolute -top-8 left-1/2 transform -translate-x-1/2 text-sm font-semibold"
        >
          {props.currency}{value[0]} - {props.currency}{value[1]}
        </div>
      )}
    </div>
  );
});
Slider.displayName = SliderPrimitive.Root.displayName;

export { Slider }

Here’s how I’m using the Slider component in my Price component:

import React, { Fragment, useEffect, useState } from "react";

// Components
import { Slider } from "@/components/ui/slider";

const Price = () => {
  const [prices, setPrices] = useState();
  const [searchByPrice, setSearchByPrice] = useState();

  async function getPrices() {
    let response = await fetch("/api/filters/get-prices", {
      method: "POST"
    });
    let data = await response.json();
    setPrices(data.data);
  }

  useEffect(() => {
    getPrices()
  }, [])

  const handleValueChange = (_prices) => {
    setSearchByPrice(_prices);
  };

  return (
    <Fragment>
      <div className="bg-light-gray px-5 pt-5 pb-7 rounded-2xl">
        <h6 className="text-base font-semibold mb-10">Price</h6>
        {prices &&
          <Slider
            defaultValue={[prices[0].default_min_price, prices[0].default_max_price]}
            max={prices[0].max_price}
            step={1} 
            currency="₹" 
            onValueChange={handleValueChange}
          />
        }
      </div>
    </Fragment>
  );
};

export default Price;
 2  16  2
1 Jan 1970

Solution

 0

You have to change value with defaultValue:

// ...rest
<SliderPrimitive.Root
  ref={ref}
  className={cn("relative flex w-full touch-none select-none items-center", className)}
  defaultValue={value} // use defaultValue
  onValueChange={handleChange}
  max={max}
  step={step}
  {...props}>
// ...rest
2024-07-25
Pinal Tilva