【问题标题】:Separate debounce for each React component instance每个 React 组件实例的单独去抖动
【发布时间】:2021-05-06 12:31:28
【问题描述】:

我在 React 组件中使用了 debounce 库(尝试了不同的库,但目前来自 lodash),以避免在浏览器中滚动时过于频繁地执行代码。

问题是我有多个 react 组件实例,似乎 debounce 函数在这些实例之间意外共享。因此,带有 '... some code here' 的功能代码仅在一个实例中执行,而不是在 react 组件的所有实例中执行。如果我只渲染了一个组件实例,则去抖动功能非常有用。

useEffect(() => {
    document.querySelector(props.scrollSelector!)?.addEventListener('scroll', e => {
        setViewport(props, state, e.target as HTMLDivElement, ref)
    }, true)
}, [state.obj])

const setViewport = debounce((p: Props, s: State, rowHeaderObj: any, scrollContainer: HTMLDivElement, ref: any) => {
    // ... some code here
}, 20)

有没有办法改变代码,使去抖动功能分别适用于每个实例?请考虑 react 组件实例分配了唯一的键,所以这不应该是问题。

【问题讨论】:

    标签: javascript reactjs typescript


    【解决方案1】:

    一种方法是在每次注册事件侦听器时创建一个新的去抖动函数,而不是重复使用相同的函数,在这种情况下,事件处理程序将在组件的每个实例中独立地去抖动。

    const _setViewport = () => (
      p: Props,
      s: State,
      rowHeaderObj: any,
      scrollContainer: HTMLDivElement,
      ref: any
    ) => {
      // ... some code here
    }
    
    const MyComponent: React.FC<Props> = (props) => {
      const [state, setState] = useState<State>()
      const ref = useRef<any>()
    
      useEffect(() => {
        const srollableElement = document.querySelector(props.scrollSelector!)
        if (!srollableElement) {
          return
        }
        const setViewport = debounce(_setViewport, 20)
        const scrollHandler = (e: Event) =>
          setViewport(props, state, e.target as HTMLDivElement, ref)
        srollableElement.addEventListener('scroll', scrollHandler, true)
        return () => {
          srollableElement.removeEventListener('scroll', scrollHandler, true)
        }
      }, [state, props, ref])
    
      return <></>
    }
    

    附带说明,请注意useEffect 的这种用法,因为(我认为)传递给组件的props 参数会在每次父组件重新渲染时发生变化,从而导致 useEffect 可能重新经常跑。对此的一种解决方法是确保传递给useEffect 的依赖项数组仅包含原始值或稳定值。随意阅读 React 文档的 this section 以讨论该主题。考虑到这一点,您可能需要将上面的示例重写如下(取决于 Props 类型的形状):

    interface Props {
      scrollSelector?: string
      b: string
      c: number
    }
    
    const _setViewport = () => (
      p: Props,
      s: State,
      rowHeaderObj: any,
      scrollContainer: HTMLDivElement,
      ref: any
    ) => {
      // ... some code here
    }
    
    const MyComponent: React.FC<Props> = ({ scrollSelector, b, c }) => {
      const [state, setState] = useState<State>()
      const ref = useRef<any>()
    
      useEffect(() => {
        if (!scrollSelector) {
          return
        }
        const srollableElement = document.querySelector(scrollSelector)
        if (!srollableElement) {
          return
        }
        const setViewport = debounce(_setViewport, 20)
        const scrollHandler = (e: Event) =>
          setViewport(
            { scrollSelector, b, c },
            state,
            e.target as HTMLDivElement,
            ref
          )
        srollableElement.addEventListener('scroll', scrollHandler, true)
        return () => {
          srollableElement.removeEventListener('scroll', scrollHandler, true)
        }
      }, [state, scrollSelector, b, c, ref])
    
      return <></>
    }
    

    【讨论】:

    • 还有其他方法吗?我在一般 Input 组件中有 debounce 功能,所以我不想从上面为每个实例传递它。
    • 我不确定您所说的“通用输入组件”是什么意思。也许值得提出一个新问题,举一个具体的例子来说明你的意思?
    猜你喜欢
    • 2017-06-16
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 2019-01-17
    • 2023-03-09
    • 1970-01-01
    • 2021-06-30
    • 2018-02-05
    相关资源
    最近更新 更多