Skip to content

Slider onChangeEnd callback does not update with state changes, when component is controlled #7620

@danieledelgiudice

Description

@danieledelgiudice

Dependencies check up

  • I have verified that I use latest version of all @mantine/* packages

What version of @mantine/* packages do you have in package.json?

^7.17.2

What package has an issue?

@mantine/core

What framework do you use?

Vite

In which browsers you can reproduce the issue?

Chrome

Describe the bug

It looks like the Slider component’s onChangeEnd callback may be using a memoized version of the function that is bound during the initial render and not updating it when the component re-renders with new props or state.

This causes a scenario where, even if the onChangeEnd handler depends on a state variable and is recreated correctly (e.g., using useCallback with the appropriate dependencies), it still seems to log the initial state instead of the updated one.

Interestingly, making the Slider uncontrolled (by removing the value and onChange props) appears to fix the issue, which suggests the problem might be related to how the slider manages controlled props.

Additionally, when controlling the slider using arrow keys on the keyboard, the updated callback reference is used and the component works as expected. The issue seems to occur specifically when interacting with the slider via mouse clicks.

Additional issue – Hot Module Replacement (HMR)

Another side effect of this behavior is that hot module replacement (HMR) is broken. When updating the onChangeEnd callback during development, the new handler is not used unless the component is unmounted and remounted or the page is manually refreshed.

Steps to reproduce:

  1. Create a Slider component with value and onChange props to control its value.
  2. Pass a callback function to the onChangeEnd prop that logs a state variable.
  3. Update the state variable by clicking the button.
  4. Interact via mouse events with the slider to trigger the onChangeEnd callback.
  5. Observe that the logged state remains stuck at the initial value, even though the callback function should reflect the updated state.

Here’s a minimal example demonstrating the issue:

import React, { useState, useCallback } from 'react';
import { Slider } from '@mantine/core';

function Demo() {
  const [value, setValue] = useState(0);
  const [state, setState] = useState(10);

  const handleChangeEnd = useCallback(() => {
    console.log('state', state);
  }, [state]);

  return (
    <Stack>
      <Slider
        value={value}
        onChange={setValue}
        onChangeEnd={handleChangeEnd}
      />
      <Button
        type='button'
        onClick={() => setState(prev => prev + 1)}
      >
        Increase state ({state})
      </Button>
    </Stack>
  );
}

Expected behavior:

The onChangeEnd callback should reflect the latest value of state on each render, as per standard React behavior.

Actual behavior:

The onChangeEnd callback logs the initial value of state and does not update even after the state changes.

If possible, include a link to a codesandbox with a minimal reproduction

https://codesandbox.io/p/sandbox/mantine-react-template-forked-w3jvvz

Possible fix

I took a look at the relevant files in the repository to try and get a better understanding of where the issue might be coming from. However, since I’m not very familiar with the internal workings of the Mantine codebase, I wasn’t able to figure out the root cause. Hopefully, someone with more knowledge of the project will be able to identify what’s going on here.

Self-service

  • I would be willing to implement a fix for this issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    Not an issueQuestion, third-party dependency, user error

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions