我正在尝试通过执行以下操作,使用useRef
钩子为`data数组中的每个项目创建引用:
const markerRef = useRef(data.posts.map(React.createRef))
现在,data
是通过GraphQL从外部获取的,它需要一些时间才能到达,因此,在安装阶段,data
是undefined
。这将导致以下错误:
TypeError:无法读取未定义的属性“ 0”
我尝试以下操作均未成功:
const markerRef = useRef(data && data.posts.map(React.createRef))
如何设置以便可以通过data
进行映射而不会导致错误?
useEffect(() => {
handleSubmit(navigation.getParam('searchTerm', 'default value'))
}, [])
const [loadItems, { called, loading, error, data }] = useLazyQuery(GET_ITEMS)
const markerRef = useRef(data && data.posts.map(React.createRef))
const onRegionChangeComplete = newRegion => {
setRegion(newRegion)
}
const handleSubmit = () => {
loadItems({
variables: {
query: search
}
})
}
const handleShowCallout = index => {
//handle logic
}
if (called && loading) {
return (
<View style={[styles.container, styles.horizontal]}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
)
}
if (error) return <Text>Error...</Text>
return (
<View style={styles.container}>
<MapView
style={{ flex: 1 }}
region={region}
onRegionChangeComplete={onRegionChangeComplete}
>
{data && data.posts.map((marker, index) => (
<Marker
ref={markerRef.current[index]}
key={marker.id}
coordinate={{latitude: marker.latitude, longitude: marker.longitude }}
// title={marker.title}
// description={JSON.stringify(marker.price)}
>
<Callout onPress={() => handleShowCallout(index)}>
<Text>{marker.title}</Text>
<Text>{JSON.stringify(marker.price)}</Text>
</Callout>
</Marker>
))}
</MapView>
</View>
)
我使用useLazyQuery
是因为我需要在不同的时间触发它。
更新:
我已根据@azundo的建议将useRef
修改为以下内容:
const dataRef = useRef(data);
const markerRef = useRef([]);
if (data && data !== dataRef.current) {
markerRef.current = data.posts.map(React.createRef);
dataRef.current = data
}
当我console.log markerRef.current
时,得到以下结果:
这很好。但是,当我尝试映射每个current
并调用showCallout()
来打开每个标记的所有标注时,请执行以下操作:
markerRef.current.map(ref => ref.current && ref.current.showCallout())
什么都不会执行。
console.log(markerRef.current.map(ref => ref.current && ref.current.showCallout()))
这显示每个数组为空。
答案 0 :(得分:2)
useRef
表达式在每次组件安装时仅执行一次,因此只要data
更改,您就需要更新引用。起初,我建议使用useEffect
,但是它运行得太晚了,因此在第一次渲染时就不会创建参考。应该改用第二个参考来检查data
是否已更改以同步重新生成标记参考。
const dataRef = useRef(data);
const markerRef = useRef([]);
if (data && data !== dataRef.current) {
markerRef.current = data.posts.map(React.createRef);
dataRef.current = data;
}
其他修改:
为了在安装的所有组件上触发showCallout
,必须先填充引用。对于useLayoutEffect
,这可能是一个合适的时间,这样它就可以在渲染标记并设置参考值(应该?)之后立即运行。
useLayoutEffect(() => {
if (data) {
markerRef.current.map(ref => ref.current && ref.current.showCallout());
}
}, [data]);
答案 1 :(得分:1)
使用备忘录创建引用,例如:
const markerRefs = useMemo(() => data && data.posts.map(d => React.createRef()), [data]);
然后将它们渲染为:
{data &&
data.posts.map((d, i) => (
<Marker key={d} data={d} ref={markerRefs[i]}>
<div>Callout</div>
</Marker>
))}
并使用ref调用命令式函数,例如:
const showAllCallouts = () => {
markerRefs.map(r => r.current.showCallout());
};
查看模拟了Marker
的工作代码:https://codesandbox.io/s/muddy-bush-gfd82