使用useSafeArea挂钩时反应本机Android TextInput问题

时间:2020-07-26 09:40:18

标签: android reactjs react-native expo

我已经按照下面的代码制作了自己的副模式。 它接受一个子组件,然后将其渲染为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>
)}

样式

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,所以可能是我的本机版本。.?

请赐教!

环境

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

1 个答案:

答案 0 :(得分:0)

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

请参见以下代码。

引起问题

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的子组件传递,则问题就不存在了。 我选择制作一个单独的包装器组件,因为子组件必须具有自己的道具,而直接编写JSX确实使代码看起来很脏。

我绝对不知道为什么要这么做,但是由于这个问题也重置了TextInput Component,我想Android设备上的Hook和Component之间会发生某种冲突。

希望此方法可以在某人遇到此问题的情况下节省3天的时间。