Использование setState в onFocus удаляет фокус - PullRequest
0 голосов
/ 23 мая 2019

Я обнаружил, что условием для изменения css на TextInput является изменение состояния компонента с помощью реквизитов onFocus и onBlur.

Я узнал об этом из этого поста .

Однако, несмотря на успех, мне пришлось сделать некрасивый обходной путь:

  1. Я переместил управление состоянием в свой компонент формы вместо того, чтобы поместить его в свою TextInput оболочку

  2. Мне пришлось реализовать хак componentDidUpdate(), чтобы перефокусировать TextInput, что-то вроде этого

componentDidUpdate() {
     this.props.isActive ? setTimeout(() => this.input.focus(), 100) : null
}

Теперь все работает, как ожидалось, за исключением того, чтоклавиатура будет мигать при переходе к следующему TextInput с использованием onSubmitEditing реквизита.

Вот мой фрагмент кода, относящийся к этому случаю:

в моем компоненте формы Я создаю экземпляр компонента-обертки вводакак в цикле, то есть fields.map()

<AuthTextInput
     ref={ref => inputRefs[key] = ref}
     key={key}
     label={label}
     onFocus={() => this.setState({ currentInput: key })}
     isActive={this.state.currentInput === key}
     onSubmitEditing={() => (idx + 1) < fields.length && fields[idx + 1].type !== 'selection' ? inputRefs[fields[idx + 1].key].focus() : this.setState({ currentInput: null })}
     blurOnSubmit={(idx + 1) === fields.length || fields[idx + 1].type === 'selection'}
     returnKeyType={(idx + 1) === fields.length || fields[idx + 1].type === 'selection' ? 'done' : 'next'}
     autoCapitalize={autocaps}
     secureTextEntry={secureTextEntry}
     placeholder={placeholder}
     keyboardType={keyboard}
     containerStyle={[{ width: orientation() === 'landscape' ? 0.5 * windowWidth() : windowWidth() * 0.7, height: normalize(70), marginVertical: normalize(10) }]}
     leftIcon={<Image style={{ width: normalize(50), height: normalize(50), marginTop: 25 }} source={fieldIcon} />}  
     onChangeText={(text) => this.onChange(key, text)}
     value={this.state[key] ? this.state[key]['value'] : ''}
     error={this.state.error[key]}
/>

Содержание AuthTextInput выглядит примерно так:

import React, { Component } from 'react';
import { View, StyleSheet, TextInput, Text } from 'react-native';
import { Input } from 'react-native-elements';

import { isEmpty } from '../utils/validate';
import { windowWidth, fontSize, fontFamily, normalize, color } from '../theme/baseTheme';
import IconWrapper from './IconWrapper';

const styles = StyleSheet.create({
    container: {
        flex: 1
    },

    inputContainer: {
        borderBottomWidth: 0,
    },

    inputStyle: {
        fontSize: fontSize.regular + 2,
        fontFamily: fontFamily.bold,
        paddingLeft: normalize(15),
        borderBottomWidth: 1
    },

    errorStyle: {
        color: color.red,
        fontSize: fontSize.small - 4,
        fontFamily: fontFamily.bold,
        fontWeight: 'bold',
        marginLeft: normalize(75)
    },
    focusedContainer: {
        borderWidth: 1,
        borderColor: color.light_blue,
        borderRadius: 8
    }
});

class AuthTextInput extends Component {
    constructor(props) {
        super(props);

        this.state = {
            secureText: this.props.secureTextEntry,
        }
    }

    componentDidUpdate() {
        this.props.isActive ? setTimeout(() => this.input.focus(), 100) : null
    }

    focus() {
        this.input.focus();
    }

    render() {
        const { secureTextEntry, value, containerStyle, isActive } = this.props;

        return (
            <Input
                {...this.props}
                ref={ref => this.input = ref}
                disableFullscreenUI={true}
                secureTextEntry={this.state.secureText}
                containerStyle={[styles.container, containerStyle, isActive ? styles.focusedContainer : null]}
                inputContainerStyle={styles.inputContainer}
                inputStyle={styles.inputStyle}
                rightIcon={
                    secureTextEntry && value !== '' ?
                        this.state.secureText ?
                            <IconWrapper name="visibility" size={20} color={color.light_grey} style={{ justifyContent: 'center' }} onPress={() => this.setState({ secureText: false })} />
                            :
                            <IconWrapper name="visibility-off" size={20} color={color.light_grey} style={{ justifyContent: 'center' }} onPress={() => this.setState({ secureText: true })} />
                        :
                        null
                }
                errorMessage={!isEmpty(this.props.error) ? this.props.error : null}
                errorStyle={[styles.errorStyle, this.props.errorStyle]}
            />
        );
    }
}

export default AuthTextInput;

Проблема, которую я обнаружил, в основном заключается в первом фрагменте, где я написалonFocus={() => this.setState({ currentInput: key })}, который повторно отображает компонент формы и некоторыекак убрать фокус.Следовательно, перефокусировка в AuthTextInput componentDidUpdate.

Я думал, что когда мой компонент формы перерисовывается, все старые AuthTextInput разрушаются, поэтому я попытался сделать autoFocus={this.props.isActive} тоже в AuthTextInput, но он не был успешным, потому что сам componentDidMount никогда не вызывался, что означает, что они не были уничтожены, просто обновлены.

Это заставило меня задуматься, не уничтожили ли они и не переделали ли ониЧто заставило фокус уйти?Я имею в виду, что я сделал то же самое, чтобы установить значение, выполнив onChangeText={(text) => this.onChange(key, text)} и вызвав setState.и в этом случае компонент не потерял фокус.

В любом случае, я хотел бы знать, может ли кто-либо из них:

  1. показать мне, на что направлен фокуспрочь ИЛИ

  2. с помощью моего обходного пути, описанного выше, для перефокусировки после установки состояния формы, предотвращения мерцания клавиатуры (исчезновения и повторного появления в течение короткого интервала).

Заранее спасибо!

ОБНОВЛЕНИЕ:

Я обнаружил, что в моей обертке TextInput, когда она обновляется в ПЕРВЫЙ РАЗ, используя setState из onFocus обратного вызова, он всегда вызывает onBlur сразу, что странно, и я не смог найти ничего, что вызывает onBlur в первый раз ни в моем, ни в коде библиотеки react-native-element.

UPDATE2:

Получается дажекогда я уже отключил onBlur от вызова и обновления компонента еще раз сразу после onFocus, ввод все еще теряет фокус, и я понятия не имею, что его вызывает.Я проверил, что и форма, и компонент ввода делали обновление, и они не сработали, странно ... Поэтому я думаю, что мне просто нужно выяснить, что крадет фокус, когда мой onFocus обновляет состояние ввода

FINAL UPDATE:ИНТЕРЕСНО НАЙТИ !!!isActive ? styles.focusedContainer : null это виновник, по какой-то причине это вызывает blur() событие.Ни один из ответов ниже не может воссоздать это, потому что ни один из них не изменяет стиль CSS этого компонента.Я думаю, это происходит потому, что containerStyleProps передается, когда родительский компонент View переходит к фактическому TextInput компоненту react-native-elements.Это вызывает рендеринг на этом уровне, в результате чего TextInput также рендеринг.Но проблема сохраняется, как мне решить эту проблему, если простое обновление моего стиля запускает повторную визуализацию TextInput?Есть ли способ подключиться к крюку TextInput shouldUpdateComponent()?

Еще раз спасибо за любое мнение, опубликованное здесь, которое помогло мне получить это понимание

1 Ответ

0 голосов
/ 23 мая 2019

Я попытался реализовать каркас проблемы.В приведенном ниже примере ввод не теряет фокус, работает как положено.

class App extends React.Component {
  state = {
    isFocus: 'no focus'
  }
  
  handleFocus = () => {
    this.setState({ isFocus: 'got focus' })
  }
  
  handleBlur = () => {
      this.setState({ isFocus: 'no focus' })
  }
   
  
  render() {
    return <input onFocus={this.handleFocus} onBlur={this.handleBlur} value={this.state.isFocus} />;
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
input:focus {
  box-shadow: 5px 5px 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
...