Reactjs React useRef挂钩导致“无法读取未定义的属性“0”错误

Reactjs React useRef挂钩导致“无法读取未定义的属性“0”错误,reactjs,react-native,react-hooks,react-native-maps,Reactjs,React Native,React Hooks,React Native Maps,我正在尝试通过执行以下操作,使用useRef钩子为`数据数组中的每个项创建引用: const markerRef = useRef(data.posts.map(React.createRef)) markerRef.current.map(ref => ref.current && ref.current.showCallout()) 现在,数据是通过GraphQL从外部获取的,需要时间才能到达,因此,在装载阶段,数据是未定义的。这会导致以下错误: TypeError

我正在尝试通过执行以下操作,使用useRef钩子为`数据数组中的每个项创建引用:

const markerRef = useRef(data.posts.map(React.createRef))
markerRef.current.map(ref => ref.current && ref.current.showCallout())
现在,数据是通过GraphQL从外部获取的,需要时间才能到达,因此,在装载阶段,数据是未定义的。这会导致以下错误:

TypeError:无法读取未定义的属性“0”

我尝试了以下方法,但没有成功:

const markerRef = useRef(data && data.posts.map(React.createRef))
如何设置,以便在不导致错误的情况下映射数据

    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>
    )
当我输入console.log markerRef.current时,会得到以下结果:

这很好。但是,当我尝试映射每个当前标记并调用showCallout以通过执行以下操作打开每个标记的所有标注时:

const markerRef = useRef(data.posts.map(React.createRef))
markerRef.current.map(ref => ref.current && ref.current.showCallout())
什么都不会被执行

console.log(markerRef.current.map(ref => ref.current && ref.current.showCallout()))
这将为每个数组显示null。

每个组件装载只执行一次useRef表达式,因此您需要在数据更改时更新REF。起初我建议使用useEffect,但它运行得太晚,因此在第一次渲染时不会创建参照。使用第二个ref检查数据是否发生了更改,以便同步重新生成标记ref,这应该是可行的

const dataRef = useRef(data);
const markerRef = useRef([]);
if (data && data !== dataRef.current) {
  markerRef.current = data.posts.map(React.createRef);
  dataRef.current = data;
}
附加编辑:

要在安装的所有组件上触发showCallout,必须首先填充REF。这可能是useLayoutEffect的适当时间,以便它在呈现标记后立即运行,并且ref值应该是?准备好了

useLayoutEffect(() => {
  if (data) {
    markerRef.current.map(ref => ref.current && ref.current.showCallout());
  }
}, [data]);
useRef表达式在每个组件装载时只执行一次,因此您需要在数据更改时更新refs。起初我建议使用useEffect,但它运行得太晚,因此在第一次渲染时不会创建参照。使用第二个ref检查数据是否发生了更改,以便同步重新生成标记ref,这应该是可行的

const dataRef = useRef(data);
const markerRef = useRef([]);
if (data && data !== dataRef.current) {
  markerRef.current = data.posts.map(React.createRef);
  dataRef.current = data;
}
附加编辑:

要在安装的所有组件上触发showCallout,必须首先填充REF。这可能是useLayoutEffect的适当时间,以便它在呈现标记后立即运行,并且ref值应该是?准备好了

useLayoutEffect(() => {
  if (data) {
    markerRef.current.map(ref => ref.current && ref.current.showCallout());
  }
}, [data]);

使用备忘录创建参照,如:

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

请参阅使用模拟标记的工作代码:

使用备忘录创建引用,如:

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

请参阅使用模拟标记的工作代码:

每个markerRef.current的当前值为null,我无法访问属性,即ShowCallout,我想我理解问题所在-效果异步运行,因此不会为数据的第一次渲染创建引用。实际上,它应该是一个记忆功能,而不是一种效果。让我更新一个解决方案。谢谢你。属性现在显示,包括showCallout,但当我试图通过`markerRef.current.mapMapMarker=>MapMarker.showCallout'访问它时,它说MapMarker.showCallout不是一个函数` markerRef.current的每个元素本身就是一个带有当前属性的ref,所以我认为它应该是markerRef.current.mapref=>ref.current&&ref.current.showCallout;将该代码记录为null。console.logmarkerRef.current.mapref=>ref.current&&ref.current每个markerRef.current的当前值都为null,我无法访问属性,即showCallout,我想我理解这个问题-效果是异步运行的,因此不会为数据的第一次渲染创建ref。实际上,它应该是一个记忆功能,而不是一种效果。让我更新一个解决方案。谢谢你。属性现在显示,包括showCallout,但当我试图通过`markerRef.current.mapMapMarker=>MapMarker.showCallout'访问它时,它说MapMarker.showCallout不是一个函数` markerRef.current的每个元素本身就是一个带有当前属性的ref,所以我认为它应该是markerRef.current.mapref=>ref.current&&ref.current.showCallout;将该代码记录为null。console.logmarkerRef.current.mapref=>ref.current&&ref.current也是如此