React Native 滑块 onValueChange 调用 setState 使滑块滞后

IT技术 javascript reactjs react-native
2021-04-29 12:09:55

由于 setState 更改了状态中滑块的值并发生在实际滑块移动之后,因此滑块滞后了很多。我见过人们使用去抖动来解决这个问题,但效果不佳,我觉得必须有一个更实用的解决方案。去抖动只是让问题变得不那么明显,它并没有从根本上解决它。

有任何想法吗?

<!-- language: lang-js -->
<Slider
    value={this.state.someValue}
    maximumValue={this.state.sliderMaximumValue}
    minimumValue={0}
    onValueChange={(someValue) => this.setState({someValue})}
/>
4个回答

您可以通过简单地valueprops传递<Slider />. 所以,像这样:

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

而已!:D

但!如果您需要不断显示滑块的变化值,(根据我的需要)您可以这样做:

在您的组件中为一个实际状态创建 2 个状态。一个用于显示,一个作为要传递给的实际值<Slider />(在这种情况下,您不需要注释掉valueprop)。

我有一个组件,我必须在其中显示不断变化的滑块值,而不会使滑块滞后。所以我这样做了:

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

所以,你需要通过valueprop传递实际状态,但只更新它onSlidingComplete另一方面,display在每次更改时更新状态,即将onValueChange其显示在某个文本组件中。

我希望我说清楚了。如果没有,请询​​问,我会详细说明。:)

使用您发布的代码,很难说如果您在组件中渲染一堆其他东西以及滑块,然后在每次值更改时调用 setState 是一件坏事,它会触发许多渲染,这就是它滞后的原因,因为它具有重新渲染太多了。

解决方案:

1) 如果您不需要在 UI 上反映任何事情,您可以执行 onValueChange={(someValue) => this.state.sliderValue = someValue} 这不会触发渲染,但会保留状态上的滑块值。

2)将滑块提取到它自己的纯组件并将滑块状态保持在该组件中,当您更改滑块设置状态时,将仅重新渲染滑块组件而不是整个屏幕。

希望这对你有帮助。

我建议使用onSlidingComplete而不是onValueChange因为它只会在用户操作结束时设置状态(并刷新视图)。

编辑 这不会影响您对滑块数字显示值的更新,因为 DOCS 状态:

这不是受控组件,您不需要在拖动过程中更新值。

我们可以像下面这样使用onTouchStartonTouchEnd

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;