React-native: TextInput flickering when format the text

Created on 24 Apr 2019  路  12Comments  路  Source: facebook/react-native

馃悰 Bug Report

When trying to format the value and applying the state, the text in text input is flickering:

rn-input-flickering mov

Any tips to prevent this behavior without the need to change the native code?

To Reproduce

Using the code sample:

  • Tap the text input
  • Type A
  • Type 1 repetitively

Expected Behavior

The text input should not display the invalid character.

Code Example

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

export default class App extends React.Component {
  state = {
    text: ''
  }

  render() {
    return (
      <View style={styles.container}>
        <TextInput
          value={this.state.text}
          onChangeText={text => {

            text = text.replace(/[0-9]/g, '')

            this.setState({
              text
            })
          }}
          style={styles.input}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  input: {
    width: '90%',
    height: 50,
    borderColor: 'gray',
    borderWidth: 1
  }
});

Environment

React Native Environment Info:
    System:
      OS: macOS 10.14.4
      CPU: (8) x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
      Memory: 612.89 MB / 16.00 GB
      Shell: 3.2.57 - /bin/bash
    Binaries:
      Node: 10.14.1 - ~/.nvm/versions/node/v10.14.1/bin/node
      Yarn: 1.12.3 - /usr/local/bin/yarn
      npm: 6.9.0 - ~/.nvm/versions/node/v10.14.1/bin/npm
      Watchman: 4.9.0 - /usr/local/bin/watchman
    SDKs:
      iOS SDK:
        Platforms: iOS 12.2, macOS 10.14, tvOS 12.2, watchOS 5.2
      Android SDK:
        API Levels: 21, 23, 26, 27
        Build Tools: 23.0.1, 23.0.2, 23.0.3, 25.0.0, 25.0.2, 26.0.0, 26.0.2, 26.0.3, 27.0.1, 27.0.3, 28.0.3
        System Images: android-23 | Google APIs Intel x86 Atom, android-27 | Google APIs Intel x86 Atom, android-27 | Google Play Intel x86 Atom
    IDEs:
      Android Studio: 3.1 AI-173.4720617
      Xcode: 10.2.1/10E1001 - /usr/bin/xcodebuild
    npmPackages:
      react: 16.5.0 => 16.5.0
      react-native: https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz => 0.57.1
Bug TextInput Locked

Most helpful comment

I can't understand how that kind of big issue still remains unresolved. Input is the most important component for almost all kind of applications and it is obviously flickering when we try to disallow some characters using regex. Input shouldn't take any action until the state given to value prop changes, so when it is controlled. It should wait the state change but it can't. A very famous library like react-native shouldn't have that kind of big bug

All 12 comments

This the same things that happens here https://github.com/facebook/react-native/issues/23578 so closing as duplicate.
Please chime in there and help us fix it.

@thymikee I don't think it's a duplicate. The other issue specifically says it's not an issue for iOS. This issue happens on iOS for sure.

More specifically, the other issue is about a corruption of the value that ultimately ends up in state:

abcd results in AABCAABCD

In this issue, the state value is ultimately correct however for a brief period the invalid value is shown to the user.

If I had to guess its because the native code renders the text input regardless and then it's unset after the JS layer code runs. I believe that's what is happening in the componentDidUpdate of TextInput.js here.

I have same issue. I try use onKeyPress & event.preventDefault() but not work.

I believe that the solution to the problem lies in onChangeText prop
in my case i had -----> onChangeText={onChange} {...restInput}
removing {...restInput} did the job.
All the best dear reader. May you design something great :)

I can't understand how that kind of big issue still remains unresolved. Input is the most important component for almost all kind of applications and it is obviously flickering when we try to disallow some characters using regex. Input shouldn't take any action until the state given to value prop changes, so when it is controlled. It should wait the state change but it can't. A very famous library like react-native shouldn't have that kind of big bug

Isn't there a solution found yet? I also have this issue. I'll explain how I begin to have this issue. I added stack navigator to my code. After that I customized my code according to it. After that I started to get this issue.

`class LoginForm extends Component {

onEmailChange(text) {
    // console.log(text);
    this.props.emailChanged(text);
}

onPasswordChange(text) {
    this.props.passwordChanged(text);
}

onLogin() {
    const { email, password, user } = this.props;

    this.props.loginUser({ email, password });
}

UNSAFE_componentWillReceiveProps(nextProps) {
    console.log(nextProps.email);
}

renderButton() {
    if (this.props.loading) {
        return <Spinner size="large" />;
    }

    return (
        <Button onPress={this.onLogin.bind(this)}>
            Login
        </Button>
    );
}

render() {
    return (
        <Card>
            <CardSection>
                <Input
                    label="Email"
                    placeholder="[email protected]"
                    onChangeText={this.onEmailChange.bind(this)}
                    value={this.props.email}
                    secureTextEntry={false}
                />
            </CardSection>
            <CardSection>
                <Input
                    secureTextEntry={true}
                    label="Password"
                    placeholder="Password"
                    onChangeText={this.onPasswordChange.bind(this)}
                    value={this.props.password}
                />
            </CardSection>
            <Text style={styles.errorTextStyle}>
                {this.props.error}
            </Text>
            <CardSection>
                {this.renderButton()}
            </CardSection>
        </Card>
    );
}

}

const styles = {
errorTextStyle: {
fontSize: 20,
alignSelf: 'center',
color: 'red'
}
}

const mapStateToProps = state => {
return {
email: state.auth.email,
password: state.auth.password,
error: state.auth.error,
loading: state.auth.loading,
user: state.auth.user
}
}

export default connect(mapStateToProps, { emailChanged, passwordChanged, loginUser })(LoginForm);`

Here is my code which has the text inputs. So I added it to my App.js file like below.

`class App extends Component {

render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));

return (
  <Provider store={store}>
    <LoginForm />
  </Provider>
);

}
}

export default App;`

My text inputs were fine until this moment. After that I added stack and drawer navigators by creating another file.

`const SecondNavigator = createDrawerNavigator({
Profile: {
screen: TaskList,
navigationOptions: ({ navigation }) => ({
title: 'Your Schedule'
})
}
}, {
// drawerType: 'slide',
});

const MainNavigator = createStackNavigator({
// Open: { screen: OpenWindow },
Home: {
screen: LoginForm,
navigationOptions: ({ navigation }) => ({
title: 'Sign In',
headerStyle: { backgroundColor: '#0680EC', height: 45 },
headerLeft: null,
headerRight: null
})
},
Profile: {
screen: SecondNavigator,
navigationOptions: ({ navigation }) => ({
title: 'Your Schedule',
headerStyle: { backgroundColor: '#0680EC', height: 45 },
headerLeft: ,
headerRight:
})
}
});

const Router = createAppContainer(MainNavigator);

const mapStateToProps = state => {
return {
email: state.auth.email,
password: state.auth.password,
error: state.auth.error,
loading: state.auth.loading,
user: state.auth.user
}
}

export default connect(mapStateToProps, { emailChanged, passwordChanged, loginUser, logOutUser })(Router);`

Then I added this file to the App.js since login form is already imported in this file.

`class App extends Component {

render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));

return (
  <Provider store={store}>
    <Router />
  </Provider>
);

}
}

export default App;`

This is when I started to have this issue. Navigation was fine. Every other operation works fine. But text input is Flickering.

If anyone has an idea of how to solve it, please advise.

I also have this issue und would love it to be solved or at least learn about a workaround! Cheers

I think I found an answer to this issue, and almost due to sheer luck, I must say. The answer is short: NEVER use value to render the value of TextInput. Use defaultValue instead. Tada! That solved my flickering problem! =D

In the example posted by OP, render function should have been:

render() {
    return (
      <View style={styles.container}>
        <TextInput
          defaultValue={this.state.text}  // -> Here! This is all we need to modify.
          onChangeText={text => {

            text = text.replace(/[0-9]/g, '')

            this.setState({
              text
            })
          }}
          style={styles.input}
        />
      </View>
    );
  }

The flickering when trying to prevent certain characters from being inputted is still a problem for me on 0.61.4. I tried @lgenzelis idea, but that doesn't work, my regex code is ignored. I get this on both iOS and Android. Could this ticket be reopened?

import React, {useState} from 'react';
import {
  TextInput,
  NativeSyntheticEvent,
  TextInputChangeEventData,
} from 'react-native';

const NUMBERS_ONLY_REGEX = RegExp(/^(\d*(\.\d{0,2})?)?$/);

const MyTextInput: React.FC = props => {
  const [value, setValue] = useState();

  const onChangeHandler = (
    e: NativeSyntheticEvent<TextInputChangeEventData>,
  ) => {
    const text = e.nativeEvent.text;
    if (!isValidInput(text)) {
      e.preventDefault();
    } else {
      setValue(text);
    }
  };

  return (
    <TextInput
      style={{height: 40, borderColor: 'gray', borderWidth: 1}}
      onChange={onChangeHandler}
      value={value}
    />
  );
};

const isValidInput = (input: string): boolean =>
  NUMBERS_ONLY_REGEX.test(input.replace(/,/g, ''));

export default MyTextInput;

God damn.. this was an issue back when I tried react native in 2015.. cant believe it's still an issue today..

@thymikee Can you please reopen this issue? It's not the same issue as the one you referenced. This bug is a flickering that happens when you try to prevent a character from being displayed in the input onChangeText. The other issue is some android only bug with formatting of uppercase/lowercase letters..

@timurridjanovic the original issue is about flickering when formatting the text, there's no preventing default in the OP. Feel free to create a new issue with a detailed reproduction.

Was this page helpful?
0 / 5 - 0 ratings