react-native-modal: The keyboard does not pop up when the Android Textinput autoFocus is true

Environment

Platforms

only happen android

Versions

  • Android:
  • iOS:
  • react-native-modal:11.5.3
  • react-native:0.63.3
  • react:

Description

Reproducible Demo

Here is our modal code <Modal isVisible={edit} style={styles.modalStyle} backdropTransitionOutTiming={0} onBackdropPress={() => this.resetState()} animationIn=“fadeInUp” animationOut=“fadeOutDown” avoidKeyboard={true} > <View style={{ backgroundColor: ‘white’, width: deviceWidth, paddingVertical: 10, borderTopLeftRadius: 10, borderTopRightRadius: 10, }} > <TextInput value={content} multiline={true} autoFocus={true} placeholder={‘回复本帖:’} maxLength={140} ref={(ref) => (this.contentInput = ref)} onBlur={this.contentInputBlur} onFocus={this.contentInputFocus} underlineColorAndroid=“transparent” onChangeText={(v) => this.setState({ content: v })} style={{ height: 100, width: deviceWidth - 30, backgroundColor: ‘#F7F7FB’, textAlignVertical: ‘top’, borderRadius: 8, alignSelf: ‘center’, padding: 15, }} /> </View> </Modal>

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 15
  • Comments: 16

Most upvoted comments

It will work:

export default function YourComponent() {
  const inputRef = React.useRef(null);
  return (
    <Modal isVisible={true} onModalShow={() => input.current.focus()} >
      <TextInput ref={inputRef} />
    </Modal>
}

It’s a solution <Modal isVisible={true} onModalShow={() => { this.inputRef.blur(); this.inputRef.focus(); } > <TextInput ref={ref => inputRef = ref} /> </Modal>

This took me ages to figure out

onShow={() => {
  setTimeout(() => {
    inputRef.current?.blur();
    inputRef.current?.focus();
   }, 100);
}}

also works in an useEffect:

useEffect(() => {
  const timeout = setTimeout(() => {
    inputRef.current?.blur();
    inputRef.current?.focus();
  }, 100);
  return () => clearTimeout(timeout);
}, []);

And it only works when calling .blur() first! It also works sometimes without the setTimeout but is more reliable with it.

my solution delay to load modal const inputTitle = useRef<TextInput>() <Modal isVisible={true} onShow={() => { const timeout = setTimeout(() => { inputTitle.current.focus() }, 100); return () => clearTimeout(timeout) } > <TextInput ref={inputTitle}/>

I encounterred this issue too…

When I find time this weekend I will creat a snack with a resolution.

Further applies only for Android, did not test that for IOS! I consider this as a workaround (like others). On Emulator autofocus actually works as intended! But on physical device it focused textInput but keyboard did NOT show up.

As i am beginner i am not sure if working without useRef is desirable or not, but when i tried to use solutions above i encountered warnings that i should use forwardRef, because of functional component.

My solution using state + autofocus instead of Ref + timeouts: const [showTextInput, setShowTextInput] = useState(false);

<Modal
onShow={() => {
        setShowTextInput(true);
      }}
> 
<View>
all your components....
{showTextInput && (
            <TextInputPrimary
              value={commentText ? commentText : ""}
              setValue={(comment) => setCommentText(comment)}
              multiLine={multiLine}          
              numOfLines={numOfLines} 
              autofocus={autofocusProp} //autofocusProp = true
            />
          )}

</View>
<Modal/>

This is basically the same as @maXXCZ1 solution, but with an added twist. It doesn’t require setTimeout, seems to work every time, visually the TextInput and the modal appear to show at the same time, and the keyboard doesn’t show twice while opening, which I noticed happening with some of the other solutions.

Short Answer:

import Modal from 'react-native-modal';

const myModal = (props) => {
  const ref = useRef(null);
  const [modalShown, setModalShown] = useState(false);

  return (
    <Modal onModalShow={() => setModalShown(true)} onModalWillHide={() => setModalShown(false)}>
      <View>
        {/* TextInput_A only shows when modal fully mounted. Has autofocus. */}
        {modalShown && (
          <TextInput
            autoFocus
            ref={ref}
            style={styleTextInput}
            onChangeText={(text) => setInputValue(text)}
            placeholder={placeholder}
          />
        )}
        {/* TextInput_A sits around and looks pretty in the meantime */}
        {!modalShown && (
          <TextInput
            style={styleTextInput}
            onChangeText={(text) => setInputValue(text)}
            placeholder={placeholder}
          />
        )}
      </View>
    </Modal>
  );
};

Long Answer
import React, { useRef, useState, ReactNode, useEffect } from 'react';
import styled from 'styled-components/native';
import { Button } from 'react-native-paper';
import Modal, { ModalProps } from 'react-native-modal';
import { View, StyleProp, TextInput as RNGHTextInput, TextStyle, ViewStyle, StyleSheet } from 'react-native';
import { TextPaper } from '../TextPaper';
import { windowHeight } from '../../constants';

type Props = Partial<ModalProps> & {
  children?: ReactNode;
  closeModal: () => void;
  handlePressLeft: () => void;
  handlePressRight: (textInput: string) => void | Promise<void>;
  isVisible: boolean;
  labelLeft?: string;
  labelRight?: string;
  placeholder?: string;
  setIsVisible: (show: boolean) => void;
  style?: StyleProp<ViewStyle>;
  styleModalContent?: StyleProp<ViewStyle>;
  styleTextInput?: StyleProp<TextStyle>;
  title?: string;
};

const InputModal = ({
  children,
  closeModal,
  handlePressLeft,
  handlePressRight,
  isVisible,
  labelLeft = 'Cancel',
  labelRight = 'Ok',
  placeholder,
  setIsVisible,
  style,
  styleModalContent,
  styleTextInput,
  title,
  ...rest
}: Props) => {
  const [inputValue, setInputValue] = useState(title ?? '');
  const [modalShown, setModalShown] = useState(false);
  const inputRef = useRef<RNGHTextInput>(null);

  return (
    <Modal
      animationIn='fadeIn'
      isVisible={isVisible}
      useNativeDriver={true}
      style={[styles.modal, style]}
      onModalShow={() => setModalShown(true)}
      onModalWillHide={() => setModalShown(false)}
      {...rest}
    >
      <ModalContent style={styleModalContent}>
        <TextPaper.TitleMedium>{title}</TextPaper.TitleMedium>
        {modalShown && (
          <TextInput
            autoFocus
            ref={inputRef}
            style={styleTextInput}
            onChangeText={(text) => setInputValue(text)}
            placeholder={placeholder}
          />
        )}
        {!modalShown && (
          <TextInput
            style={styleTextInput}
            onChangeText={(text) => setInputValue(text)}
            placeholder={placeholder}
          />
        )}
        <ButtonRowView>
          <Button onPress={handlePressLeft}>{labelLeft}</Button>
          <Button onPress={() => handlePressRight(inputValue)}>{labelRight}</Button>
        </ButtonRowView>
      </ModalContent>
    </Modal>
  );
};

const ButtonRowView = styled(View)`
  width: 100%;
  display: flex;
  justify-content: flex-end;
  flex-direction: row;
  flex-wrap: nowrap;
`;

const ModalContent = styled(View)`
  display: flex;
  align-items: flex-start;
  background-color: ${({ theme }) => theme.colors.secondaryContainer};
  border-radius: 16px;
  padding: 16px;
  margin-horizontal: 8px;
  row-gap: 16px;
`;

const TextInput = styled(RNGHTextInput)`
  width: 100%;
  border: 2px solid ${({ theme }) => theme.colors.onPrimary};
  color: ${({ theme }) => theme.colors.onPrimary};
  background-color: ${({ theme }) => theme.colors.primary};
  padding-horizontal: 16px;
`;

const styles = StyleSheet.create({
  modal: {
    position: 'absolute',
    top: windowHeight * 0.2,
  },
});

export type { Props as InputModalProps };

export default InputModal;

Hi it looks like 11.10.0 is still affected by this.

Any hint to start working on a PR? I’m willing to help 😃