On my TextInput change text, I detect whether the user pushed the # button for mentions.
onChangeText(text){
const suggestTrigger = text.match(/\B#[A-Za-z0-9]*$/i) //grab "#" trigger
const searchQuery = (suggestTrigger && suggestTrigger.length > 0) ? suggestTrigger[0] : null;
this.setState({
searchQuery: searchQuery
})
}
Then, in my render, I do:
<TextInput
autoCapitalize={this.state.searchQuery ? "none" : "sentences"}
autoCorrect={this.state.searchQuery ? false : true}
onChangeText={this.onChangeText}
/>
However, even when I do this, the autoCorrect does not turn off.
I still see this:
This is causing problems because oftentimes the system replaces the entire mention with a different word altogether.
How can I turn autoCorrect and autoCapitalize off when the user pushes the # button?
'
I have even tried rendering an entirely different , but the behavior remains.
TL;DR: you should close and re-launch your keyboard after the TextInput autoCorrect toggling value.
Buddy, this is not your fault, I had the same issue on autoFocus of react native TextInput component. I set a state name for the TextInput editable prop and then with the pressing pencil button I change the editable props. The designer told me after the TextInput made editable the cursor should be focused, so I use the isEditable state for autoFocus prop, see below:
state = {
isEditable: false
};
handleEdit = () => {
const { isEditable } = this.state;
this.setState({ isEditable: !isEditable });
};
<TextInput
autoFocus={isEditable}
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
/>
Then I press the edit button and it turns to:
But it is not focused and the Keyboard didn't launch, I sought and found this link, the TextInput does not change/update some of its props after componentDidMount. ☹️. Also, it is not different in iOS or Android, both of them has this issue, I used ref to focus on this TextInput after the isEditable state made true. see following code:
<TextInput
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
ref={input => {
this.input = input;
}}
/>
componentDidUpdate() {
const { isEditable } = this.state;
if (isEditable) {
this.input.focus();
}
}
And your case:
Absolutely you can not use ref because the autoCorrect should render with react native and it is not like focus() and blur() so JavaScript cannot access to it.
I make a test shape for your case, I create another button like a star for toggling autoCorrect value alongside my edit button. the filled star means autoCorrect is true and the lined star means autoCorrect is false, now see the test area code and view:
state = {
isEditable: false,
correct: true
};
handleCorrect = () => {
const { correct } = this.state;
this.setState({ correct: !correct });
};
<TextInput
autoCorrect={correct}
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
ref={input => {
this.input = input;
}}
/>
In the above photo, it is so clear the autoCorrect rendered as true, so it is enabled:
When I write a wrong Persian word the auto-correction show its suggestion, now time to press the star button:
Wow, the autoCorrection is false in the above situation but still we see the auto-correction of the cellphone. it is just like autoFocus it is rendered in the first render and after it, the TextInput could not change/update its props. suddenly I press edit button:
And I press the edit button again, so surely, you realized the autoCorrect is false now, ok now see what I saw:
The autoCorrect remained false by my double pressing edit button and now the auto-correction of device disappears completely. I don't know it is a bug or my bad understanding but I realized in this test area, for update autoCorrect value, we should do something after changing its value to close the iPhone keyboard and then re-launch it. the main thing that TextInput has issued is the launched keyboard.
For my test, when I pressed the edit button the editable prop of the TextInput is changed to false and the keyboard is closed, so when I pressed the edit button again, the TextInput get focused and keyboard re-launched with new autoCorrect value. This is The Secret.
Solution:
You should do something, to close and open again the iOS keyboard with the new autoCorrect value. for the test case that I wrote for your question, I decided to do a hybrid innovative solution, using ref and the callback of setState:
handleCorrect = () => {
const { correct } = this.state;
this.input.blur(); //-- this line close the keyboard
this.setState({ correct: !correct },
() => {
setTimeout(() => this.input.focus(), 50);
//-- above line re-launch keyboard after 50 milliseconds
//-- this 50 milliseconds is for waiting to closing keyborad finish
}
);
};
<TextInput
autoCorrect={correct}
editable={isEditable}
style={styles.textNameEditable}
defaultValue={text}
numberOfLines={1}
ref={input => {
this.input = input;
}}
/>
And after pressing the star button the keyboard close and re-launch and the auto-correction disappear completely.
Note: obviously, I summarized some other codes like destructuring and writing class or extends and etc for better human readability.
The problem isn't in your code completely(except Regex part which didn't work in my device) but how the React Native renders Keyboard.
I created a sample that along with your initial code also changes backgroundColor of the screen.
You will find that color changes instantly when '#' is entered whereas the keyboard doesn't.
Unless you reload the keyboard. For this, if you un-comment the code it dismisses keyboard once and once you tap back on textInput the new Keyboard without autoCorrect and autoCapitalize is shown.
state = {
searchQuery: null,
isFocused: true,
}
constructor(props) {
super(props);
this.onChangeText = this.onChangeText.bind(this);
}
onChangeText = (val) => {
const suggestTrigger = val.match(/#[A-Za-z0-9]*$/i) //grab "#" trigger
const searchQuery = (suggestTrigger && suggestTrigger.length > 0) ? suggestTrigger[0] : null;
// Un comment this to reload
// if(searchQuery && this.state.isFocused) {
// this.setState({
// isFocused: false
// });
// Keyboard.dismiss();
// // this.myTextInput.focus()
// }
this.setState({
searchQuery: searchQuery
})
}
render() {
const { searchQuery } = this.state
return (
<View style={[styles.container,
{
backgroundColor: searchQuery ? "red": "green"}
]}>
<TextInput
style={{width: 300, height: 50, borderWidth: 1}}
ref={(ref)=>{this.myTextInput = ref}}
autoCapitalize={searchQuery ? "none" : "sentences"}
autoCorrect={searchQuery ? false : true}
onChangeText={this.onChangeText}
/>
</View>
);
}
Since now we know the main cause of error may be a better solution can be reached.
I have 2 suggestions:
First, have you tried using the autoCorrect fallback?
spellCheck={this.state.searchQuery ? false : true}
Second, have you tried with native code (Obj-C / Swift)?
import { Platform, TextInput, View } from 'react-native';
import { iOSAutoCorrect } from './your-native-code';
const shouldWork = Platform.OS === 'ios' ? <iOSAutoCorrect /> : <TextInput
autoCapitalize={this.state.searchQuery ? "none" : "sentences"}
autoCorrect={this.state.searchQuery ? false : true}
onChangeText={this.onChangeText} />
return (<View>{shouldWork}</View>);
In iOSAutoCorrect you should use a UITextView. Then set its proper value depending on your condition:
textField.autocorrectionType = UITextAutocorrectionTypeNo; // or UITextAutocorrectionTypeYes
I have free-coded, thus the code is untested and might contain bugs.
Related
I have a search bar that has an customized input component. It works fine on another fixed nav but in the body of the page, it doesn't autofocus on single click.
I understand there are a ton of stack overflows and believe me, i've tried many of them (non jquery).
The problem: Single click on a div doesn't autofocus the input.
The device: All ios mobile (Safari and Chrome).
It does not happen on android mobile or on any of the desktops - onclick immediately focuses and brings up the keyboard on android mobile.
Here's some code to show how its setup
Input component
<ImportedSearchLIbrary
inputProp={
<InputComponent
autofocus
ref={inputRef}
/>
}
/>
^There is no problem with this component because it works just fine in that nav bar i mentioned.
This is how it is implemented in the body of a page>
const SearchComponent = () => {
const [show, setShow] = useState(false);
const wrapperRef = useRef(null);
const handleClose = () => {
setShow(false);
};
const handleClick = (e) => {
if (e && e.nativeEvent) e.nativeEvent.stopImmediatePropagation();
setShow(true);
const element = document.getElementById('searchID');
const yOffset = -72;
const yPosition = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
const screenWidth = window.innerWidth;
window.scrollTo({
top: yPosition,
behavior: 'smooth',
});
};
useClickOutside(wrapperRef, handleClose, [show]);
return (
<section id='searchId' ref={wrapperRef}>
<Icon iconName="search" className='' />
<div onClick={(e) => handleClick(e)}>
{show ? (
<SearchInput
placeholder='search me'
showsearch={show}
/>
) : (
<p>search me</p>
)}
</div>
{show && (
<Icon iconName="close" onClick={() => handleClose()} />
)}
</section>
);
};
So you can see that when <section> is clicked, show is set to true, and the searchbar AND the close icon show up. On desktop and android mobile (chrome, brave browser, mozilla and safari on desktop), when you click the section, it will show the search bar and auto focus to type.
This image shows how it looks - the red portion is the searchbar, the white background is the section, clickable area.
What I've tried so far>>
I've tried accessing the input ref like>
const inputRef = useRef(null);
<SearchInput
placeholder='search me'
showsearch={show}
inputRef={inputRef}
/>
Then in useEffect, (with and without settimeout)
useEffect(()=> {
setTimeout(() => {
if (show && inputRef) {
inputRef.current.focus();
}
}, 100);
}, [show]);
This didn't throw any errors but it didn't work.
Then, I tried the same thing in the searchinput component so when the component loads, in useEffect I set the focus to the passed ref.
Then I tried this >
document.addEventListener('mousedown', inputRef.current.focus());
These are the main things I've tried.
Now what works, is that if i ALWAYS show the input ie remove the condition show &&, but I don't want that.
Resources I found from stackOverflow but they didn't quite work>>
Can't Set Focus in Safari
ReactJS: How-to set focus to input-element when it enters the DOM?
https://www.quora.com/Regarding-the-mobile-browser-Safari-for-both-the-iPhone-and-iPad-with-JavaScript-how-can-I-launch-the-on-screen-keyboard
If more info is needed, i'd be happy to edit my Question with it.
Any help will be appreciated.
Thanks
React Native 0.59.9 with device running iOS 11, and Smart Punctuation enabled. This iOS feature automatically converts entered text into more visually-pleasing notations.
Examples:
double hyphen -- gets autoconverted to an emdash — (unicode 8212)
quotation mark " gets autoconverted to a curly quote “(unicode 8220)
etc.
Disabling Smart Punctuation (via Settings > General > Keyboard) is unfortunately not an option.
I render a basic TextInput component almost exactly as per the RN docs, with the only exception that I'm using my own onChangeText handler function to see the effect on the entered text:
import React, { Component } from 'react';
import { TextInput } from 'react-native';
function handleTextChange(value) {
// check value here after entering a '-' in the rendered TextInput.
// when initial value is set to '', received value is ascii 45
// when initial value is set to '-', received value is unicode 8212
}
export default function UselessTextInput() {
const [value, onChangeText] = React.useState('-'); // change this to '' to see the difference
return (
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => handleTextChange(text)}
value={value}
/>
);
}
Setting autoCorrect={false} and/or autoCompleteType='off' on the <TextInput> have no effect on the entered text.
Question
Is there a way to override this auto-correct behaviour so as to not change the user's inputted data?
I see that someone asked Facebook RN this question via a Github issue, but got no response.
it's not always an option but, adding:
keyboardType={'ascii-capable'}
to the TextInput fixed it for me
Indeed, this is an issue with the TextInput's iOS implementation, but I can provide a workaround for you. The trick is to check the TextInput's value for special characters and then replace those characters appropriately. See example below, where I replace every "—" with "--".
Code
const UselessTextInput = () => {
const [value, setText] = React.useState('-'); // change this to '' to see the difference
function handleTextChange (value) {
var val = value;
// check if value contains special characters
if (value.includes("—")){
//replace values appropriately
val = value.replace("—", "--");
}
//set new text
setText(val);
}
return (
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => handleTextChange(text)}
value={value}
/>
);
}
Working Example:
https://snack.expo.io/rJkj95ePB
I want to implement show password feature in TextInput in React Native 0.30.0. I've implemented 'eye' button next to TextInput which change state of passwordHidden state variable. Here is my code:
...
<View style={[styles.passwordWrapper, styles.textInputBorder]}>
<TextInput
autoCapitalize={'none'}
autoCorrect={false}
clearButtonMode={'while-editing'}
style={[styles.textInput, styles.passwordInput]}
onChangeText={(password) => this.onPasswordChange(password)}
value={this.state.password}
secureTextEntry={this.state.passwordHidden}
multiline={false}
placeholder={Strings.password}
underlineColorAndroid={Colors.surfacePrimary}
/>
<TouchableOpacity style={styles.showPasswordButton} onPress={this.onPressShowPassword}>
<EntypoIcon color={Colors.surfacePrimary} name={this.state.passwordHidden ? 'eye' : 'eye-with-line'} size={20} />
</TouchableOpacity>
</View>
...
onPressShowPassword: function () {
var previousState = this.state.passwordHidden;
requestAnimationFrame(() => {
this.setState({
passwordHidden: !previousState,
});
});
},
Here's how password TextInput looks before clicking on button.
And after clicking:
And when I tap third time and start type then password is immediately cleared. I am not changing fontFamily in styles even in entire app.
Anybody can explain what is going on? Or just how to overcome that annoying behavior.
Workaround that is working for me, is removing focus from TextInput, when user clicks show/hide password. One way to do this, is to add ref (for example ref="password") to your TextInput and then call this.refs.password.blur()
changing the fontSize works for me:
fontSize: (this.state.showPassword) ? 24 : 23
With the coming of multi-process Firefox, I have decided to revamp my addon. It is a toolbar addon that was built on XUL. Now I want to build it using the Addon SDK.
The old XUL overlay allowed for onMouseOver events for buttons. But the new addon SDK only has the one listener for click.
How can I get an onMouseOver (Hover) event for a toolbar button?
Maybe there is some way to add css (:hover) to the button element?
I found this, and am working on getting it in order, but maybe there's a better way?
Here is what I have so far:
var {Cu, Cc, Ci} = require("chrome");
Cu.import('resource://gre/modules/Services.jsm');
var aDOMWindow = Services.wm.getMostRecentWindow('navigator:browser');
aDOMWindow.addEventListener('mouseover', onSpatMouseover, true);
function onMyMouseover(event){
if (event.target.nodeName == 'toolbarbutton'){
console.log(event.explicitOriginalTarget.nodeName);
if(event.currentTarget.nodeName == '#MyButton'){
console.log("found the button");
}
}
}
But it does not yet find #MyButton.
First of all, error message you're getting already tells you how to make it work.
But it's not necessarily what you need anyway, usually sdk/view/core provides access to the underlying XUL elements through one of its 3 methods.
Here is a complete example of how to do this. There are two functions, actually, one for mouseover and one for mouseout. If you change the icon of a button using mouseover, you need mouseout to change it back to normal.
const { browserWindows } = require("sdk/windows");
const { CustomizableUI } = require('resource:///modules/CustomizableUI.jsm');
const { viewFor } = require("sdk/view/core");
const { ActionButton } = require("sdk/ui/button/action");
var myButton = ActionButton({
id: "mybutton",
label: "My Button",
icon: { "16": "./icon-16.png", "32":"./icon-32.png", "64": "./icon-64.png" },
onClick: function(state) {
console.log("My Button was clicked");
}
});
//create a mouseover effect for a control
exports.MouseOver = (whatbutton, whatwindow, whatfunction) =>{
CustomizableUI.getWidget( viewFor(whatbutton).id ).forWindow(whatwindow).node.addEventListener('mouseover', whatfunction, true);
};
exports.MouseOut = (whatbutton, whatwindow, whatfunction) =>{
CustomizableUI.getWidget( viewFor(whatbutton).id ).forWindow(whatwindow).node.addEventListener('mouseout', whatfunction, true);
};
function myMouseOverFunction(){
console.log("mousing over...");
}
function myMouseOutFunction(){
console.log("mousing out...");
}
//add events to the browser window
for(let w of browserWindows){
exports.MouseOver(mybutton, viewFor(w), myMouseOverFunction);
exports.MouseOut(mybutton, viewFor(w), onMouseOutFunction );
}
I'm working on a react-native app and need a TextInput that has similar functionality to the textview in the "messages" app on iOS—it should start out as one line and then gracefully expand to more lines until some limit (like 5 lines of text) and then start scrolling along to latest line as needed.
Took a look at the SlackTextViewController but a) seems like it has a lot of stuff that I don't want and b) I'd like to try to keep as much code in React (and out of objective-C/swift) as possible.
Edit: Just want to emphasize that I would prefer REACT (JAVASCRIPT) code, as stated above, rather than Objective-C or Swift.
I tried two different ways to do this today. Neither are the best but I thought I'd record my efforts in case they are helpful. They both definitely had the effect you are looking for, though sometime delayed with all the async communication.
1) Offscreen Text height
So just under the TextInput, I added a regular Text field with the same font and padding and such. I registered the onChange listener on the input and called setState({text: event.nativeEvent.text}). The Text filed got its value from the state. Both had onLayout Listeners. Basically, the goal was to get the height for the TextInput from the (unrestricted) Text. Then I hid the Text way offscreen
https://gist.github.com/bleonard/f7d748e89ad2a485ec34
2) Native Module
Really, I just needed the height of the content in the real UITextView. So I added a category to RCTUIManager as there are several methods there already that are helpful. I got rid of the hidden Text view. So onChange, I ask for the height and use that in the same way via the state.
https://gist.github.com/bleonard/6770fbfe0394a34c864b
3) Github PR
What I really hope is that this PR gets accepted. It looks to do something like this automatically.
https://github.com/facebook/react-native/pull/1229
Adding multiline={true} to a TextInput will allow scrolling if the amount of text exceeds the available space. You can then change the height of the TextInput by accessing the nativeEvent.contentSize.height of the event from the onChange prop.
class Comment extends Component {
state = {
text: '',
height: 25
}
onTextChange(event) {
const { contentSize, text } = event.nativeEvent;
this.setState({
text: text,
height: contentSize.height > 100 ? 100 : contentSize.height
});
}
render() {
return (
<TextInput
multiline
style={{ height: this.state.height }}
onChange={this.onTextChange.bind(this)}
value={this.state.text}
/>
);
}
}
As of Oct'17 there is a nice component from Wix to do this:
https://github.com/wix/react-native-autogrow-textinput
The usage can be very simple:
<AutoGrowingTextInput
style={styles.textInput}
placeholder="Enter text"
value={this.state.text}
onChangeText={this._handleChangeText}
/>
And there are some extra props like minHeight and maxHeight for example.
I'm using it on RN 0.47.2
Implement the UITextView's delegate method textViewDidChange and play with the rect
- (void)textViewDidChange:(UITextView *)textView {
CGSize constraintSize = CGSizeMake(textView.frame.size.width, MAXFLOAT);
CGRect textRect = [textView.text boundingRectWithSize:constraintSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:textView.font}
context:nil];
NSLog(#"Frame:%#", NSStringFromCGRect(textRect));
CGRect newRect = textView.frame;
newRect.size.height = textRect.size.height;
textView.frame = newRect;
}
#Groovietunes got almost all of it correct but as of 26-06-19 things have change a bit from his initial answer.
For one, the onChange prop on textinputs no longer returns the contentSize object, hence event.nativeEvent.contentSize will return undefined.
The fix for this is to use the onContentSizeChange prop. But there's a catch; onContentSizeChange runs only once when the component mounts thus making it impossible to dynamically get the height as it changes.
To fix this we need to make sure the value prop is set after onContentSizeChange in the hierarchy of props in order to keep obtaining the content size as the textinput grows.
At the end of it all i had a component that looked like this
<TextInput
placeholder={this.props.placeholder}
autoFocus={this.props.autoFocus}
style={[
styles.inputFlat,
{ height: this.state.height > 40 ? this.state.height : 40 }
]}
ref="inputFlat"
onFocus={() => {
this.toggleInput();
this.toggleButton();
}}
multiline={this.props.multiline}
onBlur={() => {
this.toggleInput();
this.toggleButton();
}}
onChangeText={this.props.onChangeText}
onContentSizeChange={event => {
const { contentSize } = event.nativeEvent;
this.setState({
height: contentSize.height
});
}}
value={this.state.value}
/>
if for some reason it seems broken as at the time of viewing this answer, refer to this documentation for any updates
Another solution is to check '\n' symbols and set the numberOfLines property.
Works for me.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {TextInput} from 'react-native';
export default class TextInputAutogrow extends Component {
constructor(props) {
super(props);
this._ref = null;
this.bindRef = this.bindRef.bind(this);
this.onChangeText = this.onChangeText.bind(this);
this.state = {
numberOfLines: this.getNumberOfLines()
};
}
bindRef(c) {
this._ref = c;
this.props.innerRef && this.props.innerRef(c);
}
getText() {
return typeof this.props.value === 'string' ?
this.props.value :
(
typeof this.props.defaultValue === 'string' ?
this.props.defaultValue :
''
);
}
getNumberOfLines(value) {
if (value === undefined) {
value = this.getText();
}
return Math.max(this.props.numberOfLines, value.split('\n').length - 1) + 1;
}
onChangeText(value) {
this.setState({numberOfLines: this.getNumberOfLines(value)})
}
render() {
return (
<TextInput
{...this.props}
ref={this.bindRef}
numberOfLines={this.state.numberOfLines}
onChangeText={this.onChangeText}
/>
)
}
}
TextInputAutogrow.propTypes = {
...TextInput.propTypes,
innerRef: PropTypes.func,
};
TextInputAutogrow.defaultProps = {
numberOfLines: 4,
};
For anyone wants an easy solution, using new versions of RN, currently using 0.63.4,
the team added autoGrow to the TextInput if it's multiline, which is the case.
Don't use height (StyeSheet property) for the component or the parent View/Views
Use minHeight, maxHeight instead, and if multiline is enabled, the TextInput will grow till reaching the maxHeight, then will be scrollable.
no calculation nor inner state needs to ba maintained.
Same thing apply for minWidth, maxWidth if you need to expand widly.
Since react-native-autogrow-textinput seems to be unmaintained, I would recommend using native-base <Input/> as it has properties that enables multiline and auto-height as follows:
import { Input } from 'native-base'
<Input
height={'auto'}
multiline={true}
// ...
/>