Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Native slider onValueChange calling setState makes slider lag

Since setState changes the value of the slider in the state and happens after the actual slider movement, the slider lags quite a bit. I've seen people use debouncing to fix this, but it doesn't work so well and I feel like there must be a more pragmatic solution. Debouncing simply makes the issue less apparent, it doesn't fix it at the root.

Any ideas?

<!-- language: lang-js -->
<Slider
    value={this.state.someValue}
    maximumValue={this.state.sliderMaximumValue}
    minimumValue={0}
    onValueChange={(someValue) => this.setState({someValue})}
/>
like image 496
underscore Avatar asked Feb 19 '19 09:02

underscore


2 Answers

You can achieve what you want by simply not passing in the value prop to the <Slider />. So, like this:

<!-- language: lang-js -->
<Slider
  // value={this.state.someValue} // commented out :D
  maximumValue={this.state.sliderMaximumValue}
  minimumValue={0}
  onValueChange={(someValue) => this.setState({someValue})}
/>

That's it! :D

But! If you need to constantly display a changing value of the slider, (as I needed) you can do this:

Create 2 states in your component for one actual piece of state. One to display, and one to be the actual value to be passed to the <Slider /> (and in this case you don't need to comment out the value prop).

I had a component where I had to display the constantly changing slider value, without the slider lagging. So I did this:

const [displayTotalQuantity, setDisplayTotalQuantity] = useState(FUEL_AMOUNT_MINIMUM);  
const [totalQuantity, setTotalQuantity] = useState(FUEL_AMOUNT_MINIMUM);

<Text> {displayTotalQuantity} </Text>
<Slider
   style={slider.baseStyles}
   step={STEP}
   value={totalQuantity}
   minimumValue={MINIMUM}
   maximumValue={MAXIMUM}
   minimumTrackTintColor={mainColors.irisBlue}
   maximumTrackTintColor={mainColors.sliderMaxTintGray}
   thumbTintColor={mainColors.irisBlue}
   onSlidingComplete={value => setTotalQuantity(value)}
   onValueChange={value => setDisplayTotalQuantity(value)}
 />

So, you need to pass the actual state through the value prop, but update it only onSlidingComplete. On the other hand, update the display state on each change, i.e. onValueChange and display it in some text component.

I hope I made myself clear. If not, ask and I will elaborate. :)

like image 77
Filip Savic Avatar answered Oct 04 '22 14:10

Filip Savic


With your posted code it's hard to say what's going on if you are rendering bunch of other stuff along with your slider in your component then calling setState on every value change is a bad thing it's triggering way to many renders that's why it's lagging because it has to much to re-render.

solutions:

1) if you don't need to reflect any thing on UI you can do onValueChange={(someValue) => this.state.sliderValue = someValue} this won't trigger render but will preserve slider value on state.

2) extract slider to it's own pure component and keep slider state to that component this way when you change slider set state will re-render only slider component part instead of whole screen.

hope this helps you.

like image 21
ArkaneKhan Avatar answered Oct 04 '22 13:10

ArkaneKhan