最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React Native slider onValueChange calling setState makes slider lag - Stack Overflow

programmeradmin1浏览0评论

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})}
/>

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})}
/>
Share Improve this question edited Feb 19, 2019 at 9:45 underscore asked Feb 19, 2019 at 9:42 underscoreunderscore 2854 silver badges10 bronze badges
Add a comment  | 

6 Answers 6

Reset to default 8

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. :)

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.

I suggest to use onSlidingComplete instead of onValueChange as it will set the state (and refresh the view) only when user action is ended.

EDIT This will not affect your update on the numeric shown value of the slider as DOCS states:

This is not a controlled component, you don't need to update the value during dragging.

we can use onTouchStart and onTouchEnd like below:

import React, { useState } from 'react';
import { View } from 'react-native';

import Slider from '@react-native-community/slider';

const MySlider = () => {
  const [value, setValue] = useState(0);
  const [isTouchEnded, setIsTouchEnded] = useState(false);

  return (
    <View>
      <Slider
        minimumValue={0}
        maximumValue={10}
        value={isTouchEnded ? value : 0}
        onValueChange={(e) => setValue(e)}
        onTouchEnd={() => setIsTouchEnded(true)}
        onTouchStart={() => setIsTouchEnded(false)}
      />
    </View>
  );
};

export default MySlider;

You should use setNativeProps instead. As said on this answer.

More about nativeProps.

You should get something like this:

<>
  <TextInput
    value={value.toLocaleString('pt-BR', {maximumFractionDigits: 2, minimumFractionDigits: 2})}
    onChangeText={(value) => {
      setValue(value);
    }}
    ref={inputRef} 
  />
  <Slider
    value={value}
    onValueChange={value => {
      inputRef.current?.setNativeProps({text: value});
    }}
  />
</>;

I used useRef instead of useState and it's working fine, without any lagging

发布评论

评论列表(0)

  1. 暂无评论