Skip to content

Warning: ref.measureLayout must be called with a ref to a native component #614

@dangervalentine

Description

@dangervalentine

Hi! 👋

Thanks for your work on this project!

Summary

When using NestableScrollContainer + NestableDraggableFlatList (one per section), React Native logs this warning once per nested list:

Warning: ref.measureLayout must be called with a ref to a native component.

This appears to be caused by calling measureLayout with a findNodeHandle(...) result (a node handle number) instead of a native component ref. Passing the scrollableRef.current directly fixes the warning.

I patched react-native-draggable-flatlist@4.0.3 via patch-package as a workaround.

Patch (diff)

diff --git a/node_modules/react-native-draggable-flatlist/src/components/NestableDraggableFlatList.tsx b/node_modules/react-native-draggable-flatlist/src/components/NestableDraggableFlatList.tsx
index 1559352..2ab50ad 100644
--- a/node_modules/react-native-draggable-flatlist/src/components/NestableDraggableFlatList.tsx
+++ b/node_modules/react-native-draggable-flatlist/src/components/NestableDraggableFlatList.tsx
@@ -1,8 +1,8 @@
 import React, { useRef, useState } from "react";
-import { findNodeHandle, LogBox } from "react-native";
+import { LogBox } from "react-native";
 import Animated, {
-  useDerivedValue,
-  useSharedValue,
+    useDerivedValue,
+    useSharedValue,
 } from "react-native-reanimated";
 import { DraggableFlatListProps } from "../types";
 import DraggableFlatList from "../components/DraggableFlatList";
@@ -13,97 +13,99 @@ import { useStableCallback } from "../hooks/useStableCallback";
 import { FlatList } from "react-native-gesture-handler";
 
 function NestableDraggableFlatListInner<T>(
-  props: DraggableFlatListProps<T>,
-  ref?: React.ForwardedRef<FlatList<T>>
+    props: DraggableFlatListProps<T>,
+    ref?: React.ForwardedRef<FlatList<T>>
 ) {
-  const hasSuppressedWarnings = useRef(false);
+    const hasSuppressedWarnings = useRef(false);
 
-  if (!hasSuppressedWarnings.current) {
-    LogBox.ignoreLogs([
-      "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing",
-    ]); // Ignore log notification by message
-    //@ts-ignore
-    console.reportErrorsAsExceptions = false;
-    hasSuppressedWarnings.current = true;
-  }
+    if (!hasSuppressedWarnings.current) {
+        LogBox.ignoreLogs([
+            "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing",
+        ]); // Ignore log notification by message
+        //@ts-ignore
+        console.reportErrorsAsExceptions = false;
+        hasSuppressedWarnings.current = true;
+    }
 
-  const {
-    scrollableRef,
-    outerScrollOffset,
-    setOuterScrollEnabled,
-  } = useSafeNestableScrollContainerContext();
+    const {
+        scrollableRef,
+        outerScrollOffset,
+        setOuterScrollEnabled,
+    } = useSafeNestableScrollContainerContext();
 
-  const listVerticalOffset = useSharedValue(0);
-  const [animVals, setAnimVals] = useState({});
-  const defaultHoverOffset = useSharedValue(0);
-  const [listHoverOffset, setListHoverOffset] = useState(defaultHoverOffset);
+    const listVerticalOffset = useSharedValue(0);
+    const [animVals, setAnimVals] = useState({});
+    const defaultHoverOffset = useSharedValue(0);
+    const [listHoverOffset, setListHoverOffset] = useState(defaultHoverOffset);
 
-  const hoverOffset = useDerivedValue(() => {
-    return listHoverOffset.value + listVerticalOffset.value;
-  }, [listHoverOffset]);
+    const hoverOffset = useDerivedValue(() => {
+        return listHoverOffset.value + listVerticalOffset.value;
+    }, [listHoverOffset]);
 
-  useNestedAutoScroll({
-    ...animVals,
-    hoverOffset,
-  });
+    useNestedAutoScroll({
+        ...animVals,
+        hoverOffset,
+    });
 
-  const onListContainerLayout = useStableCallback(async ({ containerRef }) => {
-    const nodeHandle = findNodeHandle(scrollableRef.current);
+    const onListContainerLayout = useStableCallback(async ({ containerRef }) => {
+        const scrollNode = scrollableRef.current;
+        const containerNode = containerRef.current;
 
-    const onSuccess = (_x: number, y: number) => {
-      listVerticalOffset.value = y;
-    };
-    const onFail = () => {
-      console.log("## nested draggable list measure fail");
-    };
-    //@ts-ignore
-    containerRef.current.measureLayout(nodeHandle, onSuccess, onFail);
-  });
+        if (!scrollNode || !containerNode) return;
 
-  const onDragBegin: DraggableFlatListProps<T>["onDragBegin"] = useStableCallback(
-    (params) => {
-      setOuterScrollEnabled(false);
-      props.onDragBegin?.(params);
-    }
-  );
+        const onSuccess = (_x: number, y: number) => {
+            listVerticalOffset.value = y;
+        };
+        const onFail = () => {
+            console.log("## nested draggable list measure fail");
+        };
+        containerNode.measureLayout(scrollNode, onSuccess, onFail);
+    });
 
-  const onDragEnd: DraggableFlatListProps<T>["onDragEnd"] = useStableCallback(
-    (params) => {
-      setOuterScrollEnabled(true);
-      props.onDragEnd?.(params);
-    }
-  );
+    const onDragBegin: DraggableFlatListProps<T>["onDragBegin"] = useStableCallback(
+        (params) => {
+            setOuterScrollEnabled(false);
+            props.onDragBegin?.(params);
+        }
+    );
 
-  const onAnimValInit: DraggableFlatListProps<T>["onAnimValInit"] = useStableCallback(
-    (params) => {
-      setListHoverOffset(params.hoverOffset);
-      setAnimVals({
-        ...params,
-        hoverOffset,
-      });
-      props.onAnimValInit?.(params);
-    }
-  );
+    const onDragEnd: DraggableFlatListProps<T>["onDragEnd"] = useStableCallback(
+        (params) => {
+            setOuterScrollEnabled(true);
+            props.onDragEnd?.(params);
+        }
+    );
+
+    const onAnimValInit: DraggableFlatListProps<T>["onAnimValInit"] = useStableCallback(
+        (params) => {
+            setListHoverOffset(params.hoverOffset);
+            setAnimVals({
+                ...params,
+                hoverOffset,
+            });
+            props.onAnimValInit?.(params);
+        }
+    );
 
-  return (
-    <DraggableFlatList
-      ref={ref}
-      onContainerLayout={onListContainerLayout}
-      activationDistance={props.activationDistance || 20}
-      scrollEnabled={false}
-      {...props}
-      outerScrollOffset={outerScrollOffset}
-      onDragBegin={onDragBegin}
-      onDragEnd={onDragEnd}
-      onAnimValInit={onAnimValInit}
-    />
-  );
+    return (
+        <DraggableFlatList
+            ref={ref}
+            onContainerLayout={onListContainerLayout}
+            activationDistance={props.activationDistance || 20}
+            scrollEnabled={false}
+            {...props}
+            outerScrollOffset={outerScrollOffset}
+            onDragBegin={onDragBegin}
+            onDragEnd={onDragEnd}
+            onAnimValInit={onAnimValInit}
+        />
+    );
 }
 
 // Generic forwarded ref type assertion taken from:
 // https://fettblog.eu/typescript-react-generic-forward-refs/#option-1%3A-type-assertion
 export const NestableDraggableFlatList = React.forwardRef(
-  NestableDraggableFlatListInner
+    NestableDraggableFlatListInner
 ) as <T>(
-  props: DraggableFlatListProps<T> & { ref?: React.ForwardedRef<FlatList<T>> }
+    props: DraggableFlatListProps<T> & { ref?: React.ForwardedRef<FlatList<T>> }
 ) => ReturnType<typeof NestableDraggableFlatListInner>;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions