Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TextInput flickering when format the text #24585

Closed
bhrott opened this issue Apr 24, 2019 · 12 comments
Closed

TextInput flickering when format the text #24585

bhrott opened this issue Apr 24, 2019 · 12 comments
Labels
Bug Component: TextInput Related to the TextInput component. Resolution: Locked This issue was locked by the bot.

Comments

@bhrott
Copy link

bhrott commented Apr 24, 2019

🐛 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
@thymikee
Copy link
Contributor

This the same things that happens here #23578 so closing as duplicate.
Please chime in there and help us fix it.

@jmathew
Copy link

jmathew commented Jul 11, 2019

@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.

@Donhv
Copy link

Donhv commented Aug 27, 2019

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

@puneetnerd
Copy link

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 :)

@edipkavvas
Copy link

edipkavvas commented Sep 23, 2019

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

@BSThisarasinghe
Copy link

BSThisarasinghe commented Oct 5, 2019

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.

@timostuebing
Copy link

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

@lgenzelis
Copy link

lgenzelis commented Oct 30, 2019

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>
    );
  }

@dwxw
Copy link

dwxw commented Nov 20, 2019

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;

@timurridjanovic

This comment has been minimized.

@timurridjanovic
Copy link

@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..

@thymikee
Copy link
Contributor

@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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug Component: TextInput Related to the TextInput component. Resolution: Locked This issue was locked by the bot.
Projects
None yet
Development

No branches or pull requests