ค้นหาเว็บไซต์

reactjs - วิธีการพิมพ์ measureLayout อย่างถูกต้องใน react-native


ฉันต้องการตั้งความสูงของ SVG ให้ใช้พื้นที่เฉพาะที่ฟอร์มด้านล่างอนุญาต เพื่อให้ทุกองค์ประกอบพอดีในหน้าต่างมุมมอง สำหรับเรื่องนั้น ฉันได้ส่งต่อ ref ไปยังคอมโพเนนต์ฟอร์มและคำนวณขนาดที่ควรใช้ด้วยฟังก์ชัน measureLayout ตามเอกสาร

measureLayout(relativeToNativeComponentRef, onSuccess, onFail) คล้ายกับ measure() แต่จะวัดมุมมองโดยสัมพันธ์กับบรรพบุรุษ ซึ่งกำหนดโดยการอ้างอิง relativeToNativeComponentRef ซึ่งหมายความว่าพิกัดที่ส่งคืนจะสัมพันธ์กับจุดกำเนิด x, y ของมุมมองบรรพบุรุษ

และตัวอย่างแสดงให้เห็นว่าพวกเขาเพียงแนบ refs สองตัวกับพ่อแม่และลูกด้วยค่าเริ่มต้นเป็น null และมันก็ทำงานได้

ใน TypeScript อาร์กิวเมนต์ตัวแรก - relativeToNativeComponentRef มีประเภทเป็น RefObject และ ref ที่อยู่บนองค์ประกอบคาดว่าจะเป็น LegacyRef | undefined และแม้ว่ารหัสจะทำงานได้ แต่ฉันไม่รู้ว่าจะพิมพ์มันอย่างถูกต้องอย่างไร และนอกจากนี้ในการโหลดครั้งแรกค่าทั้งหมดเป็น 0 0 0 0 เพียงเมื่อฉันรีเฟรชด้วยตนเองมันจึงจะแสดงผลอย่างถูกต้อง คุณสามารถให้คำแนะนำเกี่ยวกับเรื่องนี้ได้ไหม? โค้ดคือ

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>
  );
};

ขอบคุณมาก

แก้ไข: ฉันได้แก้ไข "และนอกจากนี้ในตอนโหลดครั้งแรกค่าทั้งหมดจะเป็น 0 0 0 0 เท่านั้น เมื่อฉันรีเฟรชด้วยตนเองมันจะแสดงผลอย่างถูกต้อง คุณสามารถให้คำใบ้เกี่ยวกับเรื่องนั้นได้ไหม?" โดยการใส่ [svgHeightForwardedRef.current, homeScreenContainerRef.current] ในอาร์เรย์ของ dependency แทนที่จะเป็น [svgHeightForwardedRef, homeScreenContainerRef] แต่ฉันยังต้องการความช่วยเหลือในเรื่อง TypeScript อยู่ ขอบคุณ

คำตอบ #1

ใช้ findNodeHandle เพื่อแปลงประเภทของ 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,
  },
});