React-Native TextInput for registering a user Why is the typed text is not displaying or being stored into the setInput object? - react-native

Hello I am trying to navigate creating a text input on React-Native that lets the user enter their email password and name and stores it in the object. But it is not working how I expected and I am not sure where I have gone wrong. I have looked at similar projects online but most are done with React not react-natie and even still when i try their implementation it still doesnt fix the text not displaying as i type or allowing it to be store when the user presses the register button.
import { Text, View, TextInput, Button, StyleSheet} from 'react-native';
import {useState} from 'react';
export default function Home() {
const [input, setInput] = useState({
fullName:"",
email:"",
password: "",
});
const inputHandler = (e) => {
let name = e.target.fullName
let value = e.target.value;
setInput({
...input,
[name]: value,
});
};
const registerUser = (e) => {
e.preventDefault();
console.log(input)
setInput({
fullName:"",
email: "",
password:"",
});
};
return (
<View >
<Text style={styles.welcome}>
Welcome! Please Register Below
</Text>
<TextInput style={styles.input} placeholder="Name" value={input.fullName} onChangeText={inputHandler}/>
<TextInput style={styles.input} placeholder="Email" value={input.email} onChangeText={inputHandler}/>
<TextInput style={styles.input} placeholder="Password" value={input.password} onChangeText={inputHandler} />
<View style={styles.button}>
<Button title="Register" onPress={registerUser} />
</View>
<View style={styles.button}>
<Button title="Already Registered? Click here to login" onPress={()=> register()} />
</View>
</View>
);
}
const styles = StyleSheet.create({
welcome:{
padding:10
},
input: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
borderColor:'black',
borderWidth: 2,
margin:3
},
button:{
margin:5
}
});```
I have tried this.input.fullName in the value prop but that give me an error as that is not the way i defined it with my handlers. I also could go back to just using seperate states for each input like [name,setName] [password,setPassword] ect... but I would really like to understand where my knowledge is lacking for utilizing TextInput to pass user information and storing it.
From looking through similar stackoverflow questions I also tried changing my onChangeText to this
`onChangeText={(t) =>inputHandler(t)}`
but that also doesnt solve it
Thanks!

Text is not displayed, because you react on changes, but not change state properly. onTextChanges callback return to you only text (as string), not as Event. If you need track Event you can use onChange callback.
But, I recommend to you write your logic as clean and simple as possible.
Firstly, you should separate inputHandler to independent text changes functions, because it easily to manage for now in your case. For example:
const onNameChanges = (text) => {
setInput(prev => ({
...prev,
fullName: text
}))
}
And then just assign it to props of your TextInput
<TextInput style={styles.input} placeholder="Name" value={input.fullName} onChangeText={onNameChanges}/>
That's it. You code works as expected. I prepared working example for you here
https://snack.expo.dev/BkQi-kXr-8

Related

How do I get my api data to display as a single item instead of printing vertically letter by letter?

I am starting to grasp fetch and using an API but I am trying to refer to a code source that used currencies and applying it to my own version but using an API that returns a random activity. The project I am using to better grasp my understanding is react-native expo cli
What I want it to do:
Press the search button -> returns random activity to do.
Currently, it is doing this but because my old API returns an array of objects for different currencies and my current random activity API returns just one activity I believe this is why my formatting is off but after reading and trying out different .then actions I can't seem to understand fully how to display my random activity properly instead of the multiple lines of the same activity I am currently getting I want it to display only once. (My prior question had the letters vertical which I have since fixed).
here is the link for the API i want to use https://www.boredapi.com/
here is the link for the old API https://open.er-api.com/v6/latest/USD
Thanks!
import { StyleSheet, Text, View, FlatList, SafeAreaView, Button, ScrollView } from 'react-native';
import { useEffect, useState } from 'react';
const Item = ({ item }) => {
return(
<View>
<Text>{item.value}</Text>
</View>
)
}
export default function App() {
const [data, setData] = useState([]);
var searchForActivity = () => {
fetch('http://www.boredapi.com/api/activity/')
.then((res) => res.json())
.then((json) => {
var array = Object.keys(json.activity).map((key) => ({
value: json.activity,
}));
setData(array);
});
}
useEffect(() => {
searchForActivity();
}, []);
return (
<SafeAreaView>
<ScrollView>
<View style={styles.container}>
<Text>Welcome to Activity Finder</Text>
<FlatList data = {data} renderItem={Item} />
<Button title='Search' onPress={searchForActivity} />
<StatusBar style="auto" />
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Your api call returns a single object like this
{
"activity": "Write a list of things you are grateful for",
"type": "relaxation",
"participants": 1,
"price": 0,
"link": "",
"key": "2062010",
"accessibility": 0
}
Since your data is not going to be an array you should change the data initial state to something like null.
const [data, setData] = useState(null);
You're currently looping over the keys of the activity property, which are just indices. Then map over all the keys and return an object with the property value which just has the json.activity. This does not make any sense. You can set the json directly to the state like this since you don't need an array if you only want to display one activity.
var searchForActivity = () => {
fetch("http://www.boredapi.com/api/activity/")
.then((res) => res.json())
.then((json) => {
setData(json);
});
};
By this point you can't use FlatList anymore since you data in not an array anymore, in your App you should change the FlatList with the Item component you made.
return (
<SafeAreaView>
<ScrollView>
<View style={styles.container}>
<Text>Welcome to Activity Finder</Text>
<Item item={data} />
<Button title="Search" onPress={searchForActivity} />
<StatusBar style="auto" />
</View>
</ScrollView>
</SafeAreaView>
);
Note that in your Item component you use a value property. This property does not exists on the data you get back from the api and should be changed to activity for it to show the name of the activity.
<Text>{item.value}</Text>
// change value with activity
<Text>{item.activity}</Text>

How to programmatically select text in a TextInput component

Is there a way to programmatically highlight/select text that is inside a TextInput component?
You can use selectTextOnFocus to achieve this. This will ensure that all text inside the TextInput is highlighted when the field is tapped into.
Actually you can, by accessing textInput's method by refs.
<TextInput ref={input => this.myInput = input} selectTextOnFocus style={{height: 100, width: 100}} defaultValue='Hey there' />
and where you want to select all text programmatically you can
this.myInput.focus()
works on iOS, not sure about android.
Reference : http://facebook.github.io/react-native/releases/0.45/docs/textinput.html#selectionstate
I don't know if there's a better way, but I found a workaround. The text has to be focused first. Here's an example
import React { Component } from 'react';
import { Button, TextInput, findNodeHandle } from 'react-native';
import TextInputState from 'react-native/lib/TextInputState';
class MyComponent extends Component {
render() {
return (
<View style={{ flex: 1, }}>
<Button
title="select text"
onPress={() => {
TextInputState.focusTextInput(findNodeHandle(this.inputRef))
}}
</
<TextInput
selectTextOnFocus
ref={ref => this.inputRef = ref}
/>
</View>
);
}
}
I'm here to share my findings. In a List, you might encounter that selectTextOnFocus is broken. In this case you can use this method selection. From React-Native I found this:
In my case I had trouble with the selectTextOnFocus prop in a list. So I had to add a debounce function to work with selection prop.
const [shouldSelect, setShouldSelect] = useState(undefined);
const onFocus = () =>{
setShouldSelect({start:0, end:String(text).length});
}
const focus = useCallback(_.debounce(onFocus,500),[shouldSelect]);
<TextInput
selection={shouldSelect}
onBlur={()=>setShouldSelect(undefined)}
onFocus={()=>focus()}// this is important
selectTextOnFocus
ref={r=>onRef(r)}
keyboardType={'number-pad'}
autoCorrect={false}
blurOnSubmit={false}
returnKeyType={'done'}
underlineColorIos={'transparent'}
underlineColorAndroid={'white'}
allowFontScaling={false}
value={String(text)}
/>
this.inputRef.focus() sets focus to the TextInput component, and then the flag you set in the attributes selectTextOnFocus does the rest.
Note: For those who wants to use selectTextOnFocus short answer. Actually, it works fine in IOS, but doesn't work in Android.
Thanks to Arnav Yagnik; Following is a similar approach in a functional component:
const inputRef = React.useRef(null);
React.useEffect(() => {
if (inputRef.current) {
console.log('focusing !');
inputRef.current.focus();
}
}, []);
return <TextInput
multiline
label="Amount"
selectTextOnFocus
placeholder="Write Count"
value={stockCount.toString()}
/>

Get TextInput value from different component

I have 2 react native components in my app, one is a toolbar that has a "Done" button, pressed when a user is done filling a form.
The other is the form itself from where I need to get the data.
When the user clicks "Done" I send a post request with the parameters, but I can't find a neat way to get the data.
What is best practice for this?
My toolbar:
<TopToolbar text='Upload new item'
navigator={this.props.navigator} state={this.state} donePage={'true'}/>
In the toolbar component I have the done button:
<TouchableHighlight style={styles.done} onPress={() =>{this.state.text=this.props.state.data.price}} underlayColor='#4b50f8'>
<Image source={require('./Images/Path 264.png')}/>
</TouchableHighlight>
and one of the text inputs is:
<TextInput placeholder='Price*' style={styles.text} onChangeText={(text) => { this.state.data.price = text }}></TextInput>
Use state. You need to bind the view to the model => (state). Please add your code for a better guide.
Each time that you press and new character need be saved in your state using onChangeText
Example:
class UselessTextInput extends Component {
constructor(props) {
super(props);
this.state = { text: 'Useless Placeholder' };
}
render() {
return (
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
/>
);
}
}
When you press in Done button. The TextInput value will be stored in this.state.text
For more info: https://facebook.github.io/react-native/docs/textinput.html
I think your main problem is that you need to read more about the states in the react documentation
https://facebook.github.io/react-native/docs/state.html
When you needs set the state. You should use setState not this.state.data.price = text. The state is a object with multiple keys y your needs modify one internal key inside data you need modify all data key and replace it.
Example:
In your constructor declare
this.state = { data: {price: 10, name: xxxx} };
if need modify data you should do something like.
var dataM = this.state.data;
dataM.price = 200
this.setState({ data: dataM});

setNativeProps Change Value for Text Component React Native Direct Manipulation

I want to directly update the value of a component due to performance reasons.
render(){
<View>
<Text style={styles.welcome} ref={component => this._text = component}>
Some Text
</Text>
<TouchableHighlight underlayColor='#88D4F5'
style={styles.button}>
<View>
<Text style={styles.buttonText}
onPress={this.useNativePropsToUpdate.bind(this)}>
Iam the Child
</Text>
</View>
</TouchableHighlight>
</View>
}
This is the method I use to update the text component. I dont know if I am setting the right attribute/ how to figure out which attribute to set:
useNativePropsToUpdate(){
this._text.setNativeProps({text: 'Updated using native props'});
}
Essentially trying to follow the same approach from this example:
https://rnplay.org/plays/pOI9bA
Edit:
When I attempt to explicitly assign the updated value:
this._text.props.children = "updated";
( I know this this the proper way of doing things in RN ). I get the error "Cannot assign to read only property 'children' of object'#'"
So maybe this is why it cant be updated in RN for some reason ?
Instead of attempting to change the content of <Text> component. I just replaced with <TextInput editable={false} defaultValue={this.state.initValue} /> and kept the rest of the code the same. If anyone know how you can change the value of <Text> using setNativeProps OR other method of direct manipulations. Post the answer and ill review and accept.
The text tag doesn't have a text prop, so
this._text.setNativeProps({ text: 'XXXX' })
doesn't work.
But the text tag has a style prop, so
this._text.setNativeProps({ style: { color: 'red' } })
works.
We can't use setNativeProps on the Text component, instead, we can workaround and achieve the same result by using TextInput in place of Text.
By putting pointerEvent='none' on the enclosing View we are disabling click and hence we can't edit the TextInput (You can also set editable={false} in TextInput to disbale editing)
Demo - Timer (Count changes after every 1 second)
import React, {Component} from 'react';
import {TextInput, StyleSheet, View} from 'react-native';
class Demo extends Component {
componentDidMount() {
let count = 0;
setInterval(() => {
count++;
if (this.ref) {
this.ref.setNativeProps({text: count.toString()});
}
}, 1000);
}
render() {
return (
<View style={styles.container} pointerEvents={'none'}>
<TextInput
ref={ref => (this.ref = ref)}
defaultValue={'0'}
// editable={false}
style={styles.textInput}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 0.7,
justifyContent: 'center',
alignItems: 'center',
},
textInput: {
fontSize: 60,
width: '50%',
borderColor: 'grey',
borderWidth: 1,
aspectRatio: 1,
borderRadius: 8,
padding: 5,
textAlign: 'center',
},
});
export default Demo;
As setNativeProps not solving the purpose to alter the content of <Text />, I have used below approach and is working good. Create Simple React Component like below...
var Txt = React.createClass({
getInitialState:function(){
return {text:this.props.children};
},setText:function(txt){
this.setState({text:txt});
}
,
render:function(){
return <Text {...this.props}>{this.state.text}</Text>
}
});

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}
/>