组件在 setState 调用时重新渲染

时间:2021-05-24 11:32:20

标签: javascript reactjs react-native react-hooks

反应原生 PanResponder 可拖动被 setState 中断

我有一个状态变量来跟踪秒数

const [seconds, setSeconds] = useState(0)
useEffect(() => {
      const interval = setInterval(() => {
        if (seconds > 59) {
          setSeconds(0)
        } else {
          setSeconds((seconds) => seconds + 1)
        }
      }, 1000)
    
    return () => clearInterval(interval)
 }, [seconds])

我也在使用可拖动的平移响应器

import React, { useState, useRef, ReactNode } from 'react'
import {
  StyleSheet,
  Animated,
  PanResponder,
  PanResponderGestureState,
  Text
} from 'react-native'

const Draggable = (props) => {
    const { draggable, isDropZone, moveToDropArea } = props
    const [showDraggable, setshowDraggable] = useState(true)
    const pan = useRef<Animated.AnimatedValueXY>(new Animated.ValueXY()).current
    const panResponder = PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onPanResponderMove: Animated.event([
        null,
        {
          dx: pan.x,
          dy: pan.y,
        },
      ]),
      onPanResponderRelease: (e, gesture) => {
        if (isDropZone(gesture)) {
          setshowDraggable(false)
          moveToDropArea()
        } else {
          Animated.spring(pan, {
            toValue: { x: 0, y: 0 },
            useNativeDriver: false,
          }).start()
        }
        pan.flattenOffset()
      },
    })
      return (
        <Animated.View {...panResponder.panHandlers} style={[pan.getLayout()]}>
          <Text>{seconds}</Text>
        </Animated.View>
      )
  }

当我尝试拖动 draggable 时,它​​会移动,但是当状态更新时,组件会重新渲染并且 Draggable 项回到原来的位置。

如何防止这种情况发生?

提前致谢。

2 个答案:

答案 0 :(得分:0)

试着像这样写你的 useEffect 钩子:

useEffect(() => {
    const interval = setInterval(() => {
        setSeconds(currentSeconds => currentSeconds > 59 ? 0 : currentSeconds + 1);
    }, 1000);
    
    return () => clearInterval(interval);
}, []);

这样就不会在每次秒的状态改变时触发。

但是,更改状态仍然会使您的组件重新渲染。如果你想防止重新渲染,你应该使用 useRef 而不是 useState。

const seconds = useRef(0);

useEffect(() => {
    const interval = setInterval(() => {
        seconds.current = seconds.current > 59 ? 0 : seconds.current + 1;
    }, 1000);

    return () => clearInterval(interval);
}, [seconds]);

console.log(seconds.current); // will log the last saved number of seconds before the last re-render

但是如果您需要显示秒的变化,您将需要您的组件在秒状态变化时重新渲染。在这种情况下,我建议您将滚动逻辑与另一个(可能是父)组件分开,这样它就不会在由秒的状态更改引起的重新渲染时重置。

答案 1 :(得分:0)

在 useEffect 钩子的第二个参数中传递空数组。

useEffect钩子在组件挂载到dom时运行回调函数,类似于类组件中的componentDidMount生命周期方法。

setInterval 函数每隔一秒运行一次 setSeconds 方法。

在 useEffect 钩子中,我们返回了一个带有计时器参数的 clearInterval 函数,这样当组件从 dom 中卸载时 setInterval 函数就会停止,这类似于 componentWillUnmount 方法。

空数组将在 mount 上运行 useEffect,在整个生命周期中保持沉默。

useEffect(() => {
      const interval = setInterval(() => {
        if (seconds > 59) {
          setSeconds(0)
        } else {
          setSeconds((seconds) => seconds + 1)
        }
      }, 1000)
    return () => clearInterval(interval)
 },[])