nalabof679
nalabof679

Reputation: 59

Getting FlatList item's position's coordinates

I have a FlatList, and I'm trying to get each item's x, y, width, height to do some animations. I've seen it's possible with .measure and maybe even useRef, but I'm not sure how to do it within FlatList's renderItem since I can't use hooks within it.

function MyComponent() {
  const renderItem = React.useCallback(
    ({ item, index }) => {
      const coords = // ! How can I get these?
      
      return (
        <View key={item.id} style={containerStyle}>
          <TouchableOpacity>
            <Image
              source={{ uri: item.uri }}
              style={memoizedStyles.media}
            />
          </TouchableOpacity>
        </View>
      );
    },
    [],
  );

  return (
      <FlatList
        data={myData}
        numColumns={numColumns}
        renderItem={renderItem}
      />
    );
  }
}

I'm not sure if a scrollable FlatList adds complexity here. Maybe it's best to measure onPress or something? Regardless, some help would be great, thanks!

Upvotes: 1

Views: 950

Answers (1)

motto
motto

Reputation: 3179

You're correct that you can't use hooks directly in the renderItem callback there, but you can create your own wrapper component and call that instead:

const Item = ({ item }) =>
  {
    return (
      <View key={item.id} style={containerStyle}>
        <TouchableOpacity>
          <Image
            source={{ uri: item.uri }}
            style={memoizedStyles.media}
          />
        </TouchableOpacity>
      </View>
    );
  };

  ...

    const renderItem = React.useCallback(({ item, index }) => <Item item={item} />);
  ...

So far as getting the item size is concerned, you can certainly do this using useRef and ref.measure(...) but as the React Native docs note, you can get results sooner and slightly more easily using the onLayout prop.

const Item = ({ item }) =>
  {
    const [coords, setCoords] = useState();
    return (
      <View key={item.id} style={containerStyle}
        onLayout={ e => setCoords(e.nativeEvent.layout) }>
          ...

coords will initially be undefined, but as soon as each Item renders/re-renders coords will contain {x,y,left,top,width,height}.

EDIT 1

So – the x and y values are relative to each item's View container and not super useful for you, sorry!

Looks like react-native-web also provides left and top values in the LayoutEvent that are not available on other platforms. So, we should use .measure(..) instead:

const Item = ({ item }) =>
  {
    const [coords, setCoords] = useState();
    const ref = useRef();
    useEffect(() =>
      ref.current.measure(
        (x, y, width, height, pageX, pageY) =>
           setCoords({ width, height, pageX, pageY })
      ),
      [ref,setCoords]);

    return (
      <View key={item.id} style={containerStyle} ref={ref}>
        ...

This gives pageX/pageY – the absolute coordinates on the page. There's also the measureLayout() function which can give you coordinates relative to another component.

Upvotes: 2

Related Questions