簡體   English   中英

反應 useRef 鈎子導致“無法讀取未定義的屬性'0'”錯誤

[英]React useRef hook causing “Cannot read property '0' of undefined” error

我正在嘗試通過執行以下操作為`data數組中的每個項目使用useRef鈎子創建一個引用:

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

現在, data是通過 GraphQL 從外部獲取的,需要時間才能到達,因此,在掛載階段, dataundefined 這會導致以下錯誤:

TypeError:無法讀取未定義的屬性“0”

我嘗試了以下但沒有成功:

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

如何設置以便我可以通過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>
    )

我正在使用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進行 map 並調用showCallout()通過執行以下操作打開每個標記的所有標注時:

markerRef.current.map(ref => ref.current && ref.current.showCallout())

什么都沒有被執行。

console.log(markerRef.current.map(ref => ref.current && ref.current.showCallout()))

這顯示了每個陣列的 null。

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 ,必須首先填充 refs。 這可能是useLayoutEffect的適當時間,以便它在標記呈現並設置參考值(應該?)后立即運行。

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

使用 memoisation 創建 refs,例如:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM