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

Prevent crash when keyboard opens before text input is created #21

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

laurentS
Copy link

Fixes #20.
The crash seems to be due to the keyboard trying to find the native input component before it's created. This PR fixes this by catching exceptions and retrying until it finds the input. In most cases, this shouldn't slow down anything noticeably.

@singhlovey
Copy link

singhlovey commented May 23, 2018

@laurentS

Your fixes not resolve my issue. App is now hanging.

I have made the changes in RNCustomKeyboardModule.java. After Generating build my app slow down and I am not able to switch to another tab and it stop responding.

Please help!

Thanks

@laurentS
Copy link
Author

@singhlovey can you share a bit more code around where you are seeing this hanging behaviour? I don't have any problem so far, but my colleague seems to have the same problem as you (on the same code as me).
Please note I am not a java developer, so I have very little understanding of what is actually going on there, and I'm just trying things out :)

@singhlovey
Copy link

singhlovey commented May 23, 2018

@laurentS

NOTE:- This issue occur on some of the devices and not on all devices. Also this issue is not replicated on simulator.

Basically what I understood from your PR is that you have added while loop to check and wait untill native component appears. It hangs because it doesn't find native component and stuck in while loop.

In my case, I have three different tabs and each tabs have different pages with minimum 10 fields and all that fields have custom keyboard. When I switch to another tab, all text-inputs are registered with new tag value and at
inputs
that time tag value missed which causes the whole issue.

CHECK IN SCREEN SHOT WHEN I SWITCH TO ANOTHER TAB

Alternate is we need to install or call custom keyboard only when we click on text input. this plugin provides install and uninstall functions for that.

NOTE:- I DON'T KNOW HOW TO USE THESE FUNCTIONS.

If you have any sample to install and uninstall keyboard please share.

Thanks

@laurentS
Copy link
Author

laurentS commented May 23, 2018

@singhlovey I have modified CustomTextInput in the index.js file of the keypad module like this:

export class CustomTextInput extends Component {
  static propTypes = {
    ...TextInput.propTypes,
    customKeyboardType: PropTypes.string,
  };
  onFocus = () => {
// THIS IS NEW
    install(findNodeHandle(this.input), this.props.customKeyboardType);
  }
  componentDidMount() {
    install(findNodeHandle(this.input), this.props.customKeyboardType);
  }
  componentWillReceiveProps(newProps) {
    if (newProps.customKeyboardType !== this.props.customKeyboardType) {
      install(findNodeHandle(this.input), newProps.customKeyboardType);
    }
  }
  onRef = ref => {
    this.input = ref;
  };
  render() {
    const { customKeyboardType, onFocus, ...others } = this.props;
    // NOTE THE ONFOCUS ABOVE AND BELOW
    return <TextInput {...others} onFocus={this.onFocus} ref={this.onRef}/>;
  }
}

and removed the while loop in the other file (the one I had added in my PR), but left the try/catch statement. This seems to work well for me. Can you give it a try?

I first tried to remove the calls to install in componentDidMount and componentWillReceiveProps but the I was getting more problems.

@singhlovey
Copy link

@laurentS

Okay. Let me check.

Thanks

@singhlovey
Copy link

singhlovey commented May 24, 2018

@laurentS

I have made changes as suggested by you. But the thing is custom keyboard register every time when I come to the page where custom keyboard is used.

Can you just provide me sample example on how you are using custom keyboard in your react native code.

NOTE:- In my case if I have 10 fields on one page, then custom keyboard registered 10 times even it I have not opened custom keyboard yet. I want it to call or you can say register only when I opened the custom keyboard.

I am using CustomTextinput in place of TextInput. Is there any way I can use TextInput component and when I click on input field and only then custom keyboard will open in place of android default keyboard.

I have this type of fields in each tab : -

<CustomTextInput customKeyboardType="hello" selectTextOnFocus={ true } 
		keyboardType="numeric" 
		returnKeyType ="next" 
		style={[Style.width70,{alignSelf:'center'}]} 
		underlineColorAndroid='transparent'  
		onFocus={() => this.onFocus('field_name',this.state.fieldHeight)} 
		onChangeText={(value) => this.setState({field_name: this.onChange(value)})}
		onEndEditing={ (event) => this.updateFormField(event.nativeEvent.text,'field_name') }
		value={this.delimitNumbers(this.state.field_name)}
/>
<CustomTextInput customKeyboardType="hello" selectTextOnFocus={ true } 
		keyboardType="numeric" 
		returnKeyType ="next" 
		style={[Style.width70,{alignSelf:'center'}]} 
		underlineColorAndroid='transparent'  
		onFocus={() => this.onFocus('field_name',this.state.fieldHeight)} 
		onChangeText={(value) => this.setState({field_name: this.onChange(value)})}
		onEndEditing={ (event) => this.updateFormField(event.nativeEvent.text,'field_name') }
		value={this.delimitNumbers(this.state.field_name)}
/>

<CustomTextInput customKeyboardType="hello" selectTextOnFocus={ true } 
		keyboardType="numeric" 
		returnKeyType ="next" 
		style={[Style.width70,{alignSelf:'center'}]} 
		underlineColorAndroid='transparent'  
		onFocus={() => this.onFocus('field_name',this.state.fieldHeight)} 
		onChangeText={(value) => this.setState({field_name: this.onChange(value)})}
		onEndEditing={ (event) => this.updateFormField(event.nativeEvent.text,'field_name') }
		value={this.delimitNumbers(this.state.field_name)}
/>

<CustomTextInput customKeyboardType="hello" selectTextOnFocus={ true } 
		keyboardType="numeric" 
		returnKeyType ="next" 
		style={[Style.width70,{alignSelf:'center'}]} 
		underlineColorAndroid='transparent'  
		onFocus={() => this.onFocus('field_name',this.state.fieldHeight)} 
		onChangeText={(value) => this.setState({field_name: this.onChange(value)})}
		onEndEditing={ (event) => this.updateFormField(event.nativeEvent.text,'field_name') }
		value={this.delimitNumbers(this.state.field_name)}
/>

<CustomTextInput customKeyboardType="hello" selectTextOnFocus={ true } 
		keyboardType="numeric" 
		returnKeyType ="next" 
		style={[Style.width70,{alignSelf:'center'}]} 
		underlineColorAndroid='transparent'  
		onFocus={() => this.onFocus('field_name',this.state.fieldHeight)} 
		onChangeText={(value) => this.setState({field_name: this.onChange(value)})}
		onEndEditing={ (event) => this.updateFormField(event.nativeEvent.text,'field_name') }
		value={this.delimitNumbers(this.state.field_name)}
/>

<CustomTextInput customKeyboardType="hello" selectTextOnFocus={ true } 
		keyboardType="numeric" 
		returnKeyType ="next" 
		style={[Style.width70,{alignSelf:'center'}]} 
		underlineColorAndroid='transparent'  
		onFocus={() => this.onFocus('field_name',this.state.fieldHeight)} 
		onChangeText={(value) => this.setState({field_name: this.onChange(value)})}
		onEndEditing={ (event) => this.updateFormField(event.nativeEvent.text,'field_name') }
		value={this.delimitNumbers(this.state.field_name)}
/>

<CustomTextInput customKeyboardType="hello" selectTextOnFocus={ true } 
		keyboardType="numeric" 
		returnKeyType ="next" 
		style={[Style.width70,{alignSelf:'center'}]} 
		underlineColorAndroid='transparent'  
		onFocus={() => this.onFocus('field_name',this.state.fieldHeight)} 
		onChangeText={(value) => this.setState({field_name: this.onChange(value)})}
		onEndEditing={ (event) => this.updateFormField(event.nativeEvent.text,'field_name') }
		value={this.delimitNumbers(this.state.field_name)}
/>

<CustomTextInput customKeyboardType="hello" selectTextOnFocus={ true } 
		keyboardType="numeric" 
		returnKeyType ="next" 
		style={[Style.width70,{alignSelf:'center'}]} 
		underlineColorAndroid='transparent'  
		onFocus={() => this.onFocus('field_name',this.state.fieldHeight)} 
		onChangeText={(value) => this.setState({field_name: this.onChange(value)})}
		onEndEditing={ (event) => this.updateFormField(event.nativeEvent.text,'field_name') }
		value={this.delimitNumbers(this.state.field_name)}
/>

<CustomTextInput customKeyboardType="hello" selectTextOnFocus={ true } 
		keyboardType="numeric" 
		returnKeyType ="next" 
		style={[Style.width70,{alignSelf:'center'}]} 
		underlineColorAndroid='transparent'  
		onFocus={() => this.onFocus('field_name',this.state.fieldHeight)} 
		onChangeText={(value) => this.setState({field_name: this.onChange(value)})}
		onEndEditing={ (event) => this.updateFormField(event.nativeEvent.text,'field_name') }
		value={this.delimitNumbers(this.state.field_name)}
/>

Now whenever I click on tab, each field registered as it has CustomTextInput component. I need to use TextInput inplace of CustomTextInput and call keyboard using install when click on input field or onFocus event of textInput.

Thanks

@singhlovey
Copy link

@laurentS

Please help me for the above. I am not able to move because of this.

Thanks

@singhlovey
Copy link

@laurentS

Hi I am able to fix the issue for android app. Now I am implementing the same for iOS app. Can you just provide me some hints on how to integrate on iOS app. I have implemented all the things but when I try to open the custom keyboard app crashes or hang and not works.

Please help!

Thanks

@laurentS
Copy link
Author

laurentS commented Jun 7, 2018

@singhlovey Great news if it works for you on android. Can you share your solution?
As for iOS, I've never used it, so I have no idea how to solve the problem on that side, sorry.

@singhlovey
Copy link

singhlovey commented Jun 7, 2018

@laurentS

index.js file

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import {
NativeModules,
TextInput,
findNodeHandle,
AppRegistry,
AsyncStorage,
Keyboard,
} from 'react-native';

 const { CustomKeyboard} = NativeModules;

 const {
   install, uninstall,
   insertText, backSpace, doDelete,
   moveLeft, moveRight,
   switchSystemKeyboard,
 } = CustomKeyboard;

 export {
 install, uninstall,
 insertText, backSpace, doDelete,
 moveLeft, moveRight,
 switchSystemKeyboard,
};

const keyboardTypeRegistry = {};

export function register(type, factory) {
keyboardTypeRegistry[type] = factory;
}

class CustomKeyboardContainer extends Component {

render() {

const {tag, type} = this.props;
const factory = keyboardTypeRegistry[type];
if (!factory) {
  console.warn(`Custom keyboard type ${type} not registered.`);
  return null;
}
const Comp = factory();
return <Comp tag={tag} />;

}
}

AppRegistry.registerComponent("CustomKeyboard", ()=>CustomKeyboardContainer);

export class CustomTextInput extends Component {
constructor() {
super();
this.state = {
isFocus : false,
count : 0
}
}
static propTypes = {
...TextInput.propTypes,
customKeyboardType: PropTypes.string,
};

onFocus = async () => {
if(this.state.count == 0) {
	await this.callKeyobarDismiss();
	this.state.count++;
	if(this.state.isFocus == false) {
		this.setState({
			isFocus : true
		});
		setTimeout(() => {
			this.input.focus();
			
			this.input.props.onKeyPress();
		}, 1000);
	}
} else {
	this.input.props.onKeyPress();
}
/*else {
	//this.state.count = 0;
	//this.state.isFocus = false;
}*/

}

async callKeyobarDismiss() {
	Keyboard.dismiss();
	install(findNodeHandle(this.input), this.props.customKeyboardType);
}

componentDidMount() {
this.state.count = 0;
}

componentWillReceiveProps(newProps) {
if (newProps.customKeyboardType !== this.props.customKeyboardType) {
install(findNodeHandle(this.input), newProps.customKeyboardType);
}
}
onRef = ref => {
this.input = ref;
};
render() {
const { customKeyboardType,onFocus, ...others } = this.props;
return <TextInput {...others} onFocus={this.onFocus} ref={this.onRef}/>;
}
}

My project folder file where I am using custom keyboard

<CustomTextInput customKeyboardType="hello" selectTextOnFocus={ true }
keyboardType="numeric"
returnKeyType ="next"
style={[Style.width70,{alignSelf:'center'}]}
underlineColorAndroid='transparent'
onKeyPress={() => this.onFocus('field_name',this.state.fieldHeight)}
onChangeText={(value) => this.setState({field_name: this.onChange(value)})}
onEndEditing={ (event) => this.updateFormField(event.nativeEvent.text,'field_name') }
value={this.delimitNumbers(this.state.field_name)}
/>

The above code did trick for me. This is temporary solution but I have only this way to do for android. now I am not getting any crash as I am installing custom keyboard only when user click on input field. I have used onKeyPress in place of onFocus as we cannot use onFocus two times because it is on same file.

Please let me know if any doubt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants