[英]React useRef hook causing “Cannot read property '0' of undefined” error
I'm trying to create a reference using the useRef
hook for each items within an array of `data by doing the following:我正在尝试通过执行以下操作为`data数组中的每个项目使用
useRef
钩子创建一个引用:
const markerRef = useRef(data.posts.map(React.createRef))
Now, data
is fetched externally through GraphQL and it takes time to arrive, therefore, during the mounting phase, data
is undefined
.现在,
data
是通过 GraphQL 从外部获取的,需要时间才能到达,因此,在挂载阶段, data
是undefined
。 This causes the following error:这会导致以下错误:
TypeError: Cannot read property '0' of undefined
TypeError:无法读取未定义的属性“0”
I've tried the following with no success:我尝试了以下但没有成功:
const markerRef = useRef(data && data.posts.map(React.createRef))
How do I set up so that I can map through the data
without causing the error?如何设置以便我可以通过
data
map 而不会导致错误?
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>
)
I'm using the useLazyQuery
because I need to trigger it at different times.我正在使用
useLazyQuery
因为我需要在不同的时间触发它。
Update:更新:
I have modified the useRef
to the following on the advise of @azundo:根据@azundo 的建议,我已将
useRef
修改为以下内容:
const dataRef = useRef(data);
const markerRef = useRef([]);
if (data && data !== dataRef.current) {
markerRef.current = data.posts.map(React.createRef);
dataRef.current = data
}
When I console.log markerRef.current
, I get the following result:当我 console.log
markerRef.current
时,我得到以下结果:
which is perfectly fine.这很好。 However, when I attempt to map each
current
and invoke showCallout()
to open all the callouts for each marker by doing the following:但是,当我尝试对每个
current
进行 map 并调用showCallout()
通过执行以下操作打开每个标记的所有标注时:
markerRef.current.map(ref => ref.current && ref.current.showCallout())
nothing gets executed.什么都没有被执行。
console.log(markerRef.current.map(ref => ref.current && ref.current.showCallout()))
This shows null for each array.这显示了每个阵列的 null。
The useRef
expression is only executed once per component mount so you'll need to update the refs whenever data
changes. useRef
表达式仅在每个组件挂载时执行一次,因此您需要在data
更改时更新引用。 At first I suggested useEffect
but it runs too late so the refs are not created on first render.起初我建议
useEffect
但它运行得太晚了,所以在第一次渲染时不会创建参考。 Using a second ref to check to see if data
changes in order to regenerate the marker refs synchronously should work instead.使用第二个 ref 检查
data
是否更改以同步重新生成标记 refs 应该可以代替。
const dataRef = useRef(data);
const markerRef = useRef([]);
if (data && data !== dataRef.current) {
markerRef.current = data.posts.map(React.createRef);
dataRef.current = data;
}
Additional edit:附加编辑:
In order to fire the showCallout
on all of the components on mount, the refs must populated first.为了在挂载的所有组件上触发
showCallout
,必须首先填充 refs。 This might be an appropriate time for useLayoutEffect
so that it runs immediately after the markers are rendered and ref values (should?) be set.这可能是
useLayoutEffect
的适当时间,以便它在标记呈现并设置参考值(应该?)后立即运行。
useLayoutEffect(() => {
if (data) {
markerRef.current.map(ref => ref.current && ref.current.showCallout());
}
}, [data]);
Create refs using memoisation, like:使用 memoisation 创建 refs,例如:
const markerRefs = useMemo(() => data && data.posts.map(d => React.createRef()), [data]);
Then render them like:然后像这样渲染它们:
{data &&
data.posts.map((d, i) => (
<Marker key={d} data={d} ref={markerRefs[i]}>
<div>Callout</div>
</Marker>
))}
And use the refs for calling imperative functions like:并使用 refs 调用命令式函数,例如:
const showAllCallouts = () => {
markerRefs.map(r => r.current.showCallout());
};
See the working code with mocked Marker
: https://codesandbox.io/s/muddy-bush-gfd82使用模拟
Marker
查看工作代码: https://codesandbox.io/s/muddy-bush-gfd82
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.