使用 ref.current.property 与 ref.current 反应 useEffect 依赖数组

问题描述 投票:0回答:1

我有一个 React 组件,它使用 useImperativeHandle 来公开 Google 地图街景全景实例。我的目的是让 StreetView 引用在可用后附加到 Google 地图引用,因为安装后,第一次调用 useEffect(使用空依赖数组)会将

streetViewRef.current.panorama
视为未定义。

我可以通过将

streetViewRef.current?.panorama
添加到 useEffect 依赖项来实现此功能(因为效果在设置值后重新运行),但我不太了解为什么它有效(并且在印象中裁判不能与效果一起使用)。

// StreetView.tsx
const StreetView = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    updateView,
    panorama: streetViewPanoramaRef.current,
  }));
  // ... rest of component
});
// Parent.tsx
const ParentComponent = () => {
  const streetViewRef = useRef<StreetViewHandle>(null);
  const mapRef = useRef<GoogleMap>(null);

  useEffect(() => {
    if (streetViewRef.current && mapRef.current?.map) {
      streetViewRef.current.updateView(/* ... */);
      mapRef.current.map.setStreetView(streetViewRef.current.panorama);
    }
  }, [streetViewRef.current?.panorama]); // Why is this okay?

  return (
    <>
      <GoogleMap ref={mapRef} />
      <StreetView ref={streetViewRef} />
    </>
  );
};

我有以下问题:

  1. 在我的父组件中,为什么在依赖项数组中包含
    streetViewRef.current
    是错误的,但为什么包含
    streetViewRef.current?.panorama
    可以解决我的问题?
  2. 同时,当我从依赖项数组中省略全景图时,为什么 linter 不会抱怨缺少依赖项?
  3. 解决这个问题的正确方法是什么?

谢谢:)

reactjs react-hooks
1个回答
0
投票
  1. 在我的父组件中,为什么在依赖数组中包含 streetViewRef.current 是错误的,但为什么包含 streetViewRef.current?.panorama 可以解决我的问题?

ref.current
是一个稳定的对象,创建后不会被替换。这意味着第一次渲染后,不会触发
useEffect
panorama
属性发生变化,这就是它触发
useEffect
的原因。 但是,更改
ref.current
上的属性不会导致重新渲染,因此在您的情况下,其他原因会导致重新渲染。这意味着如果您删除重新渲染的原因,则不会调用
useEffect

  1. 同时,当我从依赖项数组中省略全景图时,为什么 linter 不会抱怨缺少依赖项?

linter 知道由于 ref 没有被替换,所以闭包中的 ref 永远不会过时(指向先前的实例),因此它会忽略它。 linter 也不需要设置状态函数,因为它知道它是稳定的。

  1. 解决这个问题的正确方法是什么?

当您更改 ref 来触发重新渲染时,您可以使用

useState
并将设置状态函数作为 ref 回调函数 传递。当两个组件都安装完毕后,它们会设置其引用,并触发
useEffect
。只有当两者都准备好时,
useEffect
的条件才是
true

示例代码(未测试):

// StreetView.tsx
const StreetView = forwardRef((props, ref) => {
  // function to update parent when ready
  const streetViewPanorama = useCallback(panorama => {
    ref({
      updateView,
      panorama
    })
  }, [ref]);
  
  return (
    <div ref={streetViewPanorama}>
      content
    </div>
  )
});


// Parent.tsx
const ParentComponent = () => {
  const [streetViewRef, setStreetViewRef] = useState<StreetViewHandle>(null);
  const [mapRef, setmapRef] = useState<GoogleMap>(null);

  useEffect(() => {
    if (streetViewRef && mapRef) {
      streetViewRef.updateView(/* ... */);
      mapRef.map.setStreetView(streetViewRef.panorama);
    }
  }, [streetViewRef, mapRef]);

  return (
    <>
      <GoogleMap ref={mapRef} />
      <StreetView ref={streetViewRef} />
    </>
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.