我有一个 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} />
</>
);
};
我有以下问题:
streetViewRef.current
是错误的,但为什么包含 streetViewRef.current?.panorama
可以解决我的问题?谢谢:)
ref.current
是一个稳定的对象,创建后不会被替换。这意味着第一次渲染后,不会触发useEffect
。 panorama
属性发生变化,这就是它触发 useEffect
的原因。
但是,更改 ref.current
上的属性不会导致重新渲染,因此在您的情况下,其他原因会导致重新渲染。这意味着如果您删除重新渲染的原因,则不会调用 useEffect
。
linter 知道由于 ref 没有被替换,所以闭包中的 ref 永远不会过时(指向先前的实例),因此它会忽略它。 linter 也不需要设置状态函数,因为它知道它是稳定的。
当您更改 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} />
</>
);
};