Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly type measureLayout in react-native

I need to set the height of SVG so that it would only take as much space as the form below would allow so that all elements would fit in the viewport. For that I have forwarded ref to the form component and calculated the dimension it should take with the measureLayout function, according to the docs

measureLayout(relativeToNativeComponentRef, onSuccess, onFail) Like measure(), but measures the view relative to an ancestor, specified with relativeToNativeComponentRef reference. This means that the returned coordinates are relative to the origin x, y of the ancestor view.

And the examples show that they just attach two refs to the parent and a child with the starting value of null and it works.

In typescript the first argument - relativeToNativeComponentRef is of type RefObject<number | HostComponent> and the ref that is on the element expects to be LegacyRef | undefined and although the code works I do not know how to type it properly and also on initial load all the values are 0 0 0 0 only when I manually refresh it gets properly displayed, can you hint me with that as well? The code is

const { height: viewportHeight } = Dimensions.get("window");

export const Home: React.FC<HomeScreenProps> = ({ navigation }) => {
  const [svgHeight, setSvgHeight] = useState(0);
  const svgHeightForwardedRef = useRef<View | null>(null);
  const dispatch = useAppDispatch();
  const onSumbit = (data: UserMetrics) => {
    const { gender, age } = data;
    dispatch(setUserData(data));
    navigation.navigate("Tests", { gender, age });
  };

  const homeScreenContainerRef = useRef<number | HostComponent<unknown>>(null);

  useLayoutEffect(() => {
    if (svgHeightForwardedRef.current && homeScreenContainerRef.current) {
      svgHeightForwardedRef.current?.measureLayout(
        homeScreenContainerRef.current,
        (left, top, width, height) => {
          console.log(left, top, width, height);
          const svgHeightPerPlatform =
            Platform.OS === "web"
              ? viewportHeight - height + top
              : viewportHeight - height - top;
          setSvgHeight(svgHeightPerPlatform);
        },
        () => {
          console.log("fail");
        }
      );
    }
  }, [svgHeightForwardedRef, homeScreenContainerRef]);
  return (
    <View style={styles.container} ref={homeScreenContainerRef}>
      <View style={styles.wrapper}>
        <StatusBar style="auto" />
        <Text style={styles.welcomeText}>Test Reminder</Text>
        <View style={styles.heroWrapper}>
          <HeroSVG
            width={PLATFORM_WIDTH}
            height={svgHeight}
            preserveAspectRatio="xMidYMid meet"
          />
        </View>
        <HealthCheckForm onSubmit={onSumbit} ref={svgHeightForwardedRef} />
      </View>
    </View>
  );
};

Many thanks

EDIT: I have solved "and also on initial load all the values are 0 0 0 0 only when I manually refresh it gets properly displayed, can you hint me with that as well? " by putting [svgHeightForwardedRef.current, homeScreenContainerRef.current] in the dependancy array instead of [svgHeightForwardedRef, homeScreenContainerRef], I still need help with the typescript however, thanks.

like image 872
seven Avatar asked Feb 28 '26 06:02

seven


1 Answers

use findNodeHandle to convert the type of the ref

import {findNodeHandle, StyleSheet, Text, View} from 'react-native';

export const Home: React.FC<any> = ({}) => {
  const textContainerRef = useRef<View>(null);
  const textRef = useRef<Text>(null);
  const [measure, setMeasure] = useState<{
    left: number;
    top: number;
    width: number;
    height: number;
  } | null>(null);

  useEffect(() => {
    const textRefHandle = findNodeHandle(textContainerRef.current);
    if (textRef.current && textRefHandle) {
      textRef.current.measureLayout(
        textRefHandle,
        (left, top, width, height) => {
          setMeasure({left, top, width, height});
        },
        () => console.log('fail'),
      );
    }
  }, [measure]);

  return (
    <View style={styles.container}>
      <View ref={textContainerRef} style={styles.textContainer}>
        <Text ref={textRef}>Where am I? (relative to the text container)</Text>
      </View>
      <Text style={styles.measure}>{JSON.stringify(measure)}</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  textContainer: {
    backgroundColor: '#61dafb',
    justifyContent: 'center',
    alignItems: 'center',
    padding: 12,
  },
  measure: {
    textAlign: 'center',
    padding: 12,
  },
});
like image 198
Jacek Rojek Avatar answered Mar 01 '26 22:03

Jacek Rojek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!