【问题标题】:React useRef hook causing "Cannot read property '0' of undefined" error反应 useRef 钩子导致“无法读取未定义的属性'0'”错误
【发布时间】:2019-10-08 20:11:41
【问题描述】:

我正在尝试使用useRef 挂钩为`data 数组中的每个项目创建一个引用,方法是执行以下操作:

const markerRef = useRef(data.posts.map(React.createRef))

现在,data 是通过 GraphQL 从外部获取的,需要时间才能到达,因此,在安装阶段,dataundefined。这会导致以下错误:

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

这显示每个数组的 null。

【问题讨论】:

    标签: reactjs react-native react-hooks react-native-maps


    【解决方案1】:

    useRef 表达式仅在每个组件挂载时执行一次,因此您需要在 data 更改时更新引用。起初我建议useEffect,但它运行得太晚了,所以在第一次渲染时没有创建参考。使用第二个 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;
    }
    

    补充编辑:

    为了在挂载的所有组件上触发showCallout,必须首先填充引用。这可能是useLayoutEffect 的合适时间,以便在渲染标记并设置参考值(应该?)后立即运行。

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

    【讨论】:

    • 每个markerRef.currentcurrent 为空,我无法访问属性,即showCallout
    • 啊,我想理解这个问题 - 效果异步运行,因此不会为数据的第一次渲染创建参考。实际上,它应该是一个记忆功能,而不是一个效果。让我更新一个解决方案。
    • 谢谢你。现在显示属性,包括showCallout,但是当我尝试通过`markerRef.current.map(MapMarker => MapMarker.showCallout()), it says MapMarker.showCallout 访问它时,它不是函数`
    • markerRef.current 的每个元素本身就是一个具有current 属性的引用,所以我认为它应该是markerRef.current.map(ref =&gt; ref.current &amp;&amp; ref.current.showCallout());
    • console.logging 该代码给了我nullconsole.log(markerRef.current.map(ref =&gt; ref.current &amp;&amp; ref.current)) 也是如此
    【解决方案2】:

    使用记忆创建参考,例如:

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

    并使用 refs 来调用命令式函数,例如:

      const showAllCallouts = () => {
        markerRefs.map(r => r.current.showCallout());
      };
    

    使用模拟的Marker查看工作代码:https://codesandbox.io/s/muddy-bush-gfd82

    【讨论】:

      猜你喜欢
      • 2023-03-13
      • 1970-01-01
      • 1970-01-01
      • 2019-10-08
      • 1970-01-01
      • 2021-07-14
      • 2021-05-04
      • 2022-07-26
      • 1970-01-01
      相关资源
      最近更新 更多