Реакция перехвата useRef, вызывающая ошибку «Не удается прочитать свойство« 0 »из неопределенного» - PullRequest
1 голос
/ 08 октября 2019

Я пытаюсь создать ссылку, используя хук 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, потому что мне нужно запускать его в разное время.

Обновление:

Я изменил useRef на следующие по рекомендации @azundo:

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, яполучить следующий результат: enter image description here

, что совершенно нормально. Однако, когда я пытаюсь отобразить каждый current и вызвать showCallout(), чтобы открыть все выноски для каждого маркера, сделав следующее:

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

ничего не выполняется.

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

Это показывает ноль для каждого массива.

Ответы [ 2 ]

2 голосов
/ 08 октября 2019

Выражение 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, чтобы оно запускалось сразу после визуализации маркеров и установки значений ref (должны?).

useLayoutEffect(() => {
  if (data) {
    markerRef.current.map(ref => ref.current && ref.current.showCallout());
  }
}, [data]);
1 голос
/ 09 октября 2019

Создайте ссылки, используя памятку, например:

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 showAllCallouts = () => {
    markerRefs.map(r => r.current.showCallout());
  };

см. Рабочий код с надписью Marker: https://codesandbox.io/s/muddy-bush-gfd82

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...