简体   繁体   English

每个 React 组件实例的单独去抖动

[英]Separate debounce for each React component instance

I'm using a debounce library (tried different ones but currently the one from lodash) in a react component in order to avoid executing code too often while scrolling in the browser.我在反应组件中使用了一个去抖动库(尝试了不同的库,但目前是来自 lodash 的库),以避免在浏览器中滚动时过于频繁地执行代码。

The problem is that I have multiple instances of the react component and it seems that the debounce function is accidentally shared between those instances.问题是我有多个反应组件实例,并且似乎在这些实例之间意外共享了去抖 function。 Consequently the function code with '... some code here' is only executed in one instance and not in all instances of the react component.因此,带有“...这里的一些代码”的 function 代码仅在一个实例中执行,而不是在反应组件的所有实例中执行。 The debounce functionality works great if I have only one instance of my component rendered.如果我只渲染了一个组件实例,则去抖动功能非常有用。

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)

Is there some way to change the code so the debounce function works for each instance separately?有什么方法可以更改代码,以便去抖 function 分别适用于每个实例? Please consider that the react component instances have unique keys assigned so that should not be the issue.请考虑反应组件实例分配了唯一的键,所以这不应该是问题。

One approach could be to create a new debounced function each time you register the event listener instead of reusing the same function, in which case the event handler would be debounced independently within each instance of your component.一种方法是在每次注册事件侦听器时创建一个新的去抖动 function,而不是重复使用相同的 function,在这种情况下,事件处理程序将在组件的每个实例中独立地去抖动。

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

As a side note, be careful with this usage of useEffect , as (I think) the props parameter that's passed to your component will change each time the parent component re-renders, causing useEffect to potentially re-run very often.附带说明一下,使用useEffect时要小心,因为(我认为)传递给组件的props参数会在每次父组件重新渲染时发生变化,从而导致 useEffect 可能经常重新运行。 One fix for this is making sure the dependencies array passed to useEffect only contains primitive or stable values.对此的一种解决方法是确保传递给useEffect的依赖项数组仅包含原始值或稳定值。 Feel free to read this section of the React docs for a discussion of this topic.随意阅读 React 文档的这一部分来讨论这个话题。 Taking this into consideration, you might want to re-write the above example as follows (depending on the shape of the Props type):考虑到这一点,您可能需要将上面的示例重写如下(取决于 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 <></>
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM