React Native TextInput onSubmitEditing firing with every button press - react-native

I'm having trouble with onSubmitEditing firing with every button press of my input keyboard AND when the current page first loads (that part is especially puzzling). The TextInput code is as follows:
const TimerInput = () => {
const [ value, onChangeText ] = React.useState('');
return (
<TextInput
style={{
backgroundColor: ColorScheme.Orange.e,
borderRadius: 10,
borderWidth: 0,
fontSize: 25,
height: 60,
textAlign: 'center',
shadowColor: 'gray',
shadowRadius: 10,
width: '80%',
}}
keyboardType = 'number-pad'
onSubmitEditing = {FormatTime(value)}
onChangeText = { text => onChangeText(text) }
placeholder = { ' Hours : Minutes : Seconds ' }
returnKeyType = 'done'
value = {value}
/>
);
}
The FormatTime function simply writes to the console at the moment while I've been trying to figure this out:
FormatTime = () => {
return (
console.log('test')
);
}
The behavior I'm hoping to achieve is for this to run FormatTime only when the "Done" button is pressed to close the input keyboard. I will be completely honest in that I'm not fully certain how the TextInput is working (i.e. I'm so confused about the difference between the "value" and "text"), so I'm probably missing something obvious here. Thanks so much for your help.

Cause with every button-press (and when the page is first-loaded), there's a re-render ... with your code, it executes FormatTime .. .but it supposed to bind FormatTime as a handler for onSubmitEditing event
This way you pass a handler not a function call
onSubmitEditing = {() => FormatTime(value)}

Related

React Native TextInput onPressOut fires instantly

I am making a class for a custom TextInput, where the style will change when the field is selected, and will change back as soon as it is pressed out of. It looks as follows...
export function SoftSearchBar({
height=40,
width='100%',
fontSize=20,
fireOnChange={function(){console.log("No Change Function in place")}},
value=false,
placeholder="Placeholder",
type=null
}){
const [isActive, setActive] = useState(false)
const [style, setStyle] = useState({})
useEffect(() => {
console.log(isActive)
if (isActive){
setStyle(style => ({style: styles.softSearchActive, width: width}))
}
else{
setStyle(style => ({style: styles.softSearchInactive, width: width}))
}
}, [isActive])
return(
<View style={{height: height, flexDirection: 'row'}}>
<TextInput
value={value}
onPressIn={() => setActive(true)}
onPressOut={() => setActive(false)}
style={{...style.style, width: width, zIndex: 0, fontSize: fontSize}}
textContentType={type}
text
placeholder={placeholder}
placeholderTextColor={'black'}
autoCorrect={false}
onChangeText={text => {
fireOnChange(text)
}}
/>
</View>
)
}
Almost all of this works as expected, when the field is pressed, an outline appears indicating its selection, and the text changes color. However, onPressOut fires immediately after onPressIn, as the log will look like this as soon as I press the field
true
false
indicating that onPressOut fired, since it is the only way to setIsActive(false)
I saw some solutions recommending using onResponderRelease as opposed to onPressOut but then it just never unselects. Is there some syntax Im missing with onPressOut? This seems like a pretty simple and straightforward syntax so I am unsure
Main Issue with your code is onPressIn and onPressOut you need to change them to onFocus and onBlur
Here is a working example you can paste into this website
https://reactnative.dev/docs/textinput
You can set your default Input style and then when active you can enable the style you want.
outlineStyle: none to get rid of the default blue outline of the textinput when focused
Can also just remove handleFocus & handleBlur and move the function into the actual function calls to reduce the code further
import React from "react";
import { SafeAreaView, StyleSheet, TextInput } from "react-native";
const UselessTextInput = () => {
const [style, setStyle] = React.useState({borderWidth:2 , borderColor: 'red', outlineStyle: 'none'});
const [active, setActive] = React.useState(false)
const handleFocus = () => setActive(true)
const handleBlur = () => setActive(false)
return (
<SafeAreaView>
<TextInput
style={[styles.input, active && style]}
onFocus={handleFocus}
onBlur={handleBlur}
onChangeText={() => {}}
value={null}
placeholder="useless placeholder"
keyboardType="numeric"
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
margin: 12,
borderWidth: 1,
padding: 10,
},
});
export default UselessTextInput;

Check rendered text with enzyme

I'm using react-native and I want to test some functionality.
I want to check when I insert some text to the component like: "barak walk".
It changes to "BARAK WALK".
when it's too long compare it to "BARAK WALK...".
I don't know how to check it with enzyme( i prepare don't use snapshot this time)
Code:
export default ({ text }: Props): JSX.Element => {
return (
<View style={styles.box}>
<Text numberOfLines={1} style={cardText}>
{text}
</Text>
</View>
);
};
const styles = StyleSheet.create({
box: {
height: 160,
width: 160,
},
cardText: {
color: Colors().ALWAYS_WHITE,
textAlign: 'center',
fontSize: 12,
fontFamily: AleckFonts.SANS_MEDIUM,
letterSpacing: 0.4,
lineHeight: 15,
padding: 6,
paddingLeft: 20,
paddingRight: 20,
textTransform: 'uppercase',
},
});
I tried this one but it doesn't render the text:
test('Should check if text shown as expected', () => {
const receivedText = 'barak walk';
const expectedText = 'BARAK WALK';
const wrapper = shallow(<Card text={receivedText} />);
const textView = wrapper.find(Text).children();
expect(textView.text()).toBe(expectedText);
});
Few things to try:
(1) Don't use children at all
const textView = wrapper.find(Text); // <----take out children prop
expect(textView.text()).to.equal(expectedText);
Also because you are using CSS to change the casing of the text, it will still render as lowercase.
Example - barak walk is rendering as barak walk but due to css styling, it's shown to you in all uppercase.
So, in your assertion, if write your original test like so, it should pass:
const textView = wrapper.find(Text); // <--- try
expect(textView.text()).toBe(receivedText); //<---- receivedText is gona be lowercase
This is because receivedText will be all lowercase.
Adding ... at the end when string is too long
function handleLongString(str, desiredLength){
return str.length > desiredLength ? str.slice(0,desiredLength) + "..." : str;
}
Then you can use this in your code like so:
<Text>{ handleLongString(text) }</Text>

React Native - unable to change font when secureTextEntry is set

const entryInput = forwardRef((props, ref) => {
return (
<View
style={{
fontFamily: "roboto-regular",
color: "rgba(255,0,0,0.6)",
fontSize: hp("1.5%")
}}>
<Text style={styles.text}>{props.show_err ? props.err : null}</Text>
<TextInput
ref={ref}
style={{
borderColor:
!props.err || props.err === "" || props.err === props.empty_err
? "gray"
: "rgba(255,0,0,0.6)",
backgroundColor: "rgba(213, 213, 213, 0.1)",
borderWidth: wp("0.3%"),
borderRadius: wp("1%"),
width: wp("85%"),
height: hp("5.2%"),
fontFamily: "roboto-regular",
fontSize: hp("2%"),
fontWeight: "normal"
}}
returnKeyType={props.last ? "done" : "next"}
blurOnSubmit={props.last ? true : false}
placeholderTextColor={"gray"}
paddingLeft={wp("2%")}
paddingRight={hp("2%")}
placeholder={props.placeholder}
onSubmitEditing={() => {
if (props.next_input) {
props.next_input.current.focus();
} else if (props.action) {
props.action();
}
}}
onChangeText={(text) => {
if (props.setText) props.setText(text);
if (props.validate) props.validate(text);
}}
/>
</View>
);});
New to react native... trying to create an input field for a password.
This custom component works great, but when I add the secureTextEntry={true} the font changes for no reason (it's not roboto-regular), it doesn't even change to the default font.
I noticed that when I remove the fontFamily key from the style object then save my code and the expo client reloads, then add fontFamily again and reload again the TextInput behaves as expected and the font is the one I set (roboto-regular), however the bug reappears when manually reloading the app.
The accepted answer will work on luck. The refs can be both defined or undefined when the component is being mounted.
Add the following to your component to properly solve the issue:
const _inputRef = useRef(null);
const setRef = useCallback((node) => {
if (_inputRef.current) {
// Make sure to cleanup any events/references added to the last instance
}
if (node) {
// Check if a node is actually passed. Otherwise node would be null.
// You can now do what you need to, setNativeProps, addEventListeners, measure, etc.
node.setNativeProps({
style: { fontFamily: "Quicksand-Medium" },
});
}
// Save a reference to the node
_inputRef.current = node;
}, []);
Make sure your TextInput has this ref assigned:
<TextInput ref={setRef} ... />
Adding the following to my custom component fixed the problem:
useEffect(() => {
if (ref) {
ref.current.setNativeProps({
style: { fontFamily: "roboto-regular" }
});
}
}, []);

How to make React Native TextInput keep text after screen change

I am making an app which contains a notes field which needs to keep its input after the screen changes, but it always resets after you leave the page. Here is my constructor for the state:
this.state = {text: ""};
And my textinput declaration:
<TextInput
style={{
height: 200,
width: 250,
fontSize: 15,
backgroundColor: 'white',
}}
editable = {true}
multiline = {true}
numberofLines = {4}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
/>
I've been trying to find a way to set the state to a variable that doesn't get reinitialized on opening the page up but have no luck so far. Any advice would be appreciated!
Use AsyncStorage to persist the input value. For eg:
<TextInput
style={{
height: 200,
width: 250,
fontSize: 15,
backgroundColor: 'white',
}}
editable = {true}
multiline = {true}
numberofLines = {4}
onChangeText={(text) => {
this.setState({text});
AsyncStorage.setItem('inputKey', text); // Note: persist input
}}
value={this.state.text}
/>
Then inside componentDidMount you can check for the value and update state accordingly to reinitialise with old value.
componentDidMount() {
AsyncStorage.getItem('inputKey').then((value) => {
if (value !== null){
// saved input is available
this.setState({ text: value }); // Note: update state with last entered value
}
}).done();
}
It's because when you leave the page in certain type of navigation you trigger the "componentUnmount" and it get destroy and rebuild when you come back.
You have to store your data away from your component class. In an other class for example. A class with only one instance
...
let instance = null;
class OtherClass {
constructor() {
if(instance) {
return instance;
}
instance = this;
}
...
}
So when your user make an input save the content in that "OtherClass" and then each time you rebuild your parent component, you set the value with data previously store here.
Each time you initialize that class the content wont be erase because it will always take the previous instance.
Hope it help !

How to update a text input on change

I'm trying to update a text input as it changes but it doesn't seem to work.
Here's my simplified example:
var Message = React.createClass({
getInitialState: function() {
return {textValue: ''};
},
render: function() {
return (
<View style={[styles.container]}>
<ProfilePicture userId={this.props.userId}/>
<TextInput
ref={component => this._textInput = component}
style={{flex: 8, paddingLeft: 5, fontSize: 15}}
onChangeText={this._handleChange}
//multiline={true}
defaultValue={""}
value={this.state.textValue}
returnKeyType={"send"}
blurOnSubmit={false}
autoFocus={true}
enablesReturnKeyAutomatically={true}
onSubmitEditing={this._onSubmit}
/>
</View>
);
},
_handleChange: function(text) {
this.setState({textValue: "foo"});
},
_onSubmit: function(event) {
this._clearText();
},
_clearText: function() {
this._textInput.setNativeProps({text: ''});
},
});
I'm expecting that as soon as someone enters in some text it gets automatically altered to read "foo" but this doesn't work.
Any ideas?
UPDATE
Plot thickens,
If I call the same function for onBlur it works but only when there is no text already in the text input. If I change the function to set the value using this._textInput.setNativeProps({text: 'foo'}); instead of this.setState({textValue: "foo"}); then it works both when the text input is empty and has data.
Example:
render: function() {
return (
<TextInput
ref={component => this._textInput = component}
style={{flex: 8, paddingLeft: 5, fontSize: 15}}
onChangeText={this._handleChange}
onBlur={this._handleChange}
//multiline={true}
defaultValue={""}
value={this.state.textValue}
returnKeyType={"send"}
blurOnSubmit={false}
autoFocus={true}
enablesReturnKeyAutomatically={true}
onSubmitEditing={this._onSubmit}
/>
);
},
_handleChange: function(text) {
// what to do here check if there are youtube videos?
this._textInput.setNativeProps({text: 'foo'});
}
So in the above the _handleChange works for onBlur but not for onChangeText. Weird right?
Not really an optimal solution but looking at the react native code for react-native v 0.17.0 it looks like any changes made to the component's value during onChange don't take affect.
The code has changed on HEAD and this could fix it. https://github.com/facebook/react-native/blob/master/Libraries/Components/TextInput/TextInput.js#L542
To get around this you can wrap the code to reset the text inputs value in a setTimeout like this
var self = this;
setTimeout(function() {self._textInput.setNativeProps({text: newText}); }, 1);
This creates a new change outside of the current change event.
Like I said not an optimal solution but it works.
There is another issue that the cursor position needs to be updated if the new text is larger than the old text, this isn't available on master yet but there is a PR that looks like it is close to being merged. https://github.com/facebook/react-native/pull/2668
You need to bind your onChangeText to this. Without that in the _handleChange function "this" does not refer to the component and thus setState is not going to work the way you expect it to.
<TextInput
ref={component => this._textInput = component}
style={{flex: 8, paddingLeft: 5, fontSize: 15}}
onChangeText={this._handleChange.bind(this)} // <-- Notice the .bind(this)
//multiline={true}
defaultValue={""}
value={this.state.textValue}
returnKeyType={"send"}
blurOnSubmit={false}
autoFocus={true}
enablesReturnKeyAutomatically={true}
onSubmitEditing={this._onSubmit}
/>