繁体   English   中英

使用 useSafeArea Hook 时 React Native Android TextInput 问题

[英]React Native Android TextInput issue when using useSafeArea Hook

我已经制作了自己的模式,如下所示。 它接受一个子组件,然后将其呈现为 Modal 的内容。 它在 web 和 ios 上按预期工作但是在 Android 上,当我传递任何包含 textinput 组件的子组件时会出现问题。 当我触摸 textinput 组件时,键盘会立即出现并消失,并且当我看到值恢复到初始值时,似乎还会再次呈现子组件。

这很奇怪,因为当我传递完全相同的代码而不使其成为组件时,这根本不会发生。

更奇怪的是,我的模态在更宽的屏幕上设置为一定的宽度,而较小的屏幕在全视图中呈现模态。 仅当它处于全视图时才会出现此问题。 在横向上,键盘似乎没什么不同,没有任何问题。

如果有人可以请帮助我,我将负债累累。

SideModal.js

import React, { useEffect, useState, useRef } from "react";
import { useSafeArea } from "react-native-safe-area-context";
import { View, Animated, Dimensions } from "react-native";
import { Icon } from "react-native-elements";
import { styles } from "../../styles/globalStyle";
import { TouchableOpacity } from "react-native-gesture-handler";

//This Modal should have below props mandatory
// 1. children component, 2. isVisble, 3. setIsVisble, 4. size, 5, limit
//This Modal should always reside in SafeAreaView

const SideModal = props => {
  const slideOut = useRef(new Animated.Value(0)).current;

  const [dimensions, setDimensions] = useState({
    window: Dimensions.get("window"),
  });

  const insets = useSafeArea();

  let size = 0;
  if (dimensions.window.width < props.limit) {
    size = dimensions.window.width;
  } else {
    size = props.size;
  }

  useEffect(() => {
    Animated.timing(slideOut, {
      toValue: -size,
      duration: 300,
      useNativeDriver: true,
    }).start();
    return function cleanup() {
      Animated.timing(slideOut).reset();
    };
  }, [slideOut]);

  const disappear = () => {
    Animated.timing(slideOut, {
      toValue: 0,
      duration: 300,
      useNativeDriver: true,
    }).start(({ finished }) => {
      props.setIsVisble(null);
    });
  };

  useEffect(() => {
    Dimensions.addEventListener("change", disappear);
    return () => {
      Dimensions.removeEventListener("change", disappear);
    };
  });

  return (
    <View style={[styles.modalContainer]}>
      <View
        onStartShouldSetResponder={() => disappear()}
        style={styles.modalBackDrop}
      >
        <TouchableOpacity
          onPress={() => disappear()}
          style={{ width: "100%", height: "100%" }}
        ></TouchableOpacity>
      </View>
      <Animated.View
        style={[
          styles.modalContent,
          {
            paddingTop: insets.top,
            marginRight: -size,
            width: size,
            transform: [{ translateX: slideOut }],
          },
        ]}
      >
        <Icon
          containerStyle={{
            marginTop: 5,
            marginBottom: -47,
            zIndex: 1,
            alignSelf: "flex-end",
          }}
          iconStyle={{
            marginRight: 10,
            opacity: 0.8,
          }}
          name="clear"
          type="material"
          color="#2E394B"
          size={42}
          onPress={() => disappear()}
        />
        {props.children}
      </Animated.View>
    </View>
  );
};

export default SideModal;

Main.js - 直接 JSX 作为子组件,没问题

{isVisible == 2 && (
  <SideModal
    isVisible={isVisible}
    setIsVisble={setIsVisble}
    size={400}
    limit={600}
  >
    <View>
      <TextInput />
    </View>
  </SideModal>
)}

Main.js - 自定义组件作为子组件,是的问题..

const SampleView = () => {
  return(
    <View>
      <TextInput/>
    </View>
  )
};
{isVisible == 2 && (
  <SideModal
    isVisible={isVisible}
    setIsVisble={setIsVisble}
    size={400}
    limit={600}
  >
    <SampleView/>
  </SideModal>
)}

Styles

modalContainer: {
  position: "absolute",
  width: "100%",
  height: "100%",
  justifyContent: "center",
  alignItems: "flex-end",
  overflow: "hidden",
  zIndex: 1,
  elevation: 2,
},
modalBackDrop: {
  width: "100%",
  height: "100%",
  position: "absolute",
},
modalContent: {
  shadowOpacity: 0.75,
  shadowRadius: 10,
  shadowColor: "#cccccc",
  shadowOffset: { height: 0, width: 0 },
  elevation: 10,
  zIndex: 4,
  backgroundColor: "#ffffff",
  height: "100%",
  display: "flex",
},

我完全不知道这个问题是从哪里来的。我正在为这个项目使用 Expo SDK 所以也许我的 react-native 版本可能是问题..?

请赐教!

环境

  • 在 2 个 Android 设备和 1 个 iphone 6s 上进行测试
  • 反应原生 0.62
  • Expo SDK 38

好的,我终于找到了问题所在。

请看下面的代码。

导致问题

import React from "react";
import { Text, View, TextInput } from "react-native";
import { useSafeArea } from "react-native-safe-area-context";

const Test = () => {
  const insets = useSafeArea();
  const MyInputView = () => {

    return (
      <View>
        <TextInput placeholder="input" />
      </View>
    );
  };

  return (
    <View
      style={{
        width: "100%",
        height: "100%",
        alignItems: "center",
        justifyContent: "center",
        display: "flex",
      }}
    >
      <MyInputView /> // Pass Children Component
    </View>
  );
};

export default Test;

没问题#1

import React from "react";
import { Text, View, TextInput } from "react-native";
import { useSafeArea } from "react-native-safe-area-context";

const Test = () => {
  const insets = useSafeArea();
  const MyInputView = () => {
    return (
      <View>
        <TextInput placeholder="input" />
      </View>
    );
  };

  return (
    <View
      style={{
        width: "100%",
        height: "100%",
        alignItems: "center",
        justifyContent: "center",
        display: "flex",
      }}
    >
      <View>
        <TextInput placeholder="input" /> //Pass JSX Directly
      </View>
    </View>
  );
};

export default Test;

没问题 #2 - 我的选择

import React from "react";
import { Text, View, TextInput } from "react-native";
import { useSafeArea } from "react-native-safe-area-context";

const Test = () => {
  const Wrapper = props => {
    const insets = useSafeArea();
    return (
      <View
        style={{
          width: "100%",
          height: "100%",
          alignItems: "center",
          justifyContent: "center",
          display: "flex",
        }}
      >
        {props.children}
      </View>
    );
  };

  const MyInputView = () => {
    return (
      <View>
        <TextInput placeholder="input" />
      </View>
    );
  };

  return (
    <Wrapper>
      <MyInputView />
    </Wrapper>
  );
};

export default Test;

所以我认为这是一个错误,因为这只能在 Android 设备上重现。 可能是一个实际上已在最新的 react native 版本上修复的错误,但由于我使用的是 Expo,所以我坚持使用较低版本。

当您在屏幕中的顶级组件中使用 useSafeArea() Hook(或 useSafeAreaInsets)声明变量,然后当您使用包含 TextInput 的自定义组件时,就会出现问题。 即使你不使用变量,如果它被声明了,那也会导致问题。 如果你在单独的组件中声明钩子,或者直接将 JSX 作为包含 TextInput 的子组件传递,问题就消失了。 我选择制作一个单独的包装器组件,因为子组件必须有自己的 props 并且直接编写 JSX 确实使代码看起来很脏。

我完全不知道为什么会这样,但由于这个问题也重置了 TextInput 组件,我猜想在 Android 设备上的 Hook 和 Component 之间存在某种冲突。

如果遇到此问题,希望这可以节省某人的 3 天时间。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM