Please help me. I have a TextInput, my problem is when I type the numeric value, that value going to multiply with some number. When I type 1000, this will get saved to state as 1000, but it saves to store as 100. The results differ.
<TextInput
onChangeText={this.handle_bill_Amount}
style={styles.input}
placeholder="Amount"
value={this.state.Amount}
keyboardType = 'numeric'
enablesReturnKeyAutomatically={true}
placeholderTextColor = "#824242"
underlineColorAndroid="transparent">
</TextInput>
handle_bill_Amount = Amount => {
this.setState({ Amount})
let billamt = this.state.Amount;
console.log(billamt);
}
constructor(props) {
super(props);
this.state = {
Amount: '',
}
this.handle_bill_Amount = this.handle_bill_Amount.bind(this);
}
Guys help me please!
this.setState is async in nature, hence non-deterministic in the next statement. So, when you are logging the value, state might not yet have the updated value.
So for deterministic behavior, it is advised that you use the second argument, which is a callback, to get the correct state. It is invoked when setState is executed.
this.setState({Amount}, () => console.log(this.state.Amount))
I think results are not different, it's just your console.log which confuses you. It will print out 100 instead of 1000 because your are doing console log inside the function which is setting the new state. Try to do it somewhere else, e.g. in componentWillReceiveProps, or componentWillUpdate and you'll see it's ok.
Hope this helps.
Related
If I change text of TextInput then I change useState named name and setName.
const [name, setName] = useState("")
<TextInput
defaultValue={meData?.me?.name}
onChangeText={(text) => setNameValue(text)}
/>
And I want to change disableConfirm state from true to first, if I change just one word in this TextInput with useEffect.
const [disableConfirm, setDisableConfirm] = useState(true);
useEffect(() => {
setDisableConfirm(false)
}, [nameValue]);
The problem is when screen is first rendered, this useEffect is executed.
So disableConfirm state becomes false even though I don't change any word in TextInput.
How to prevent first rendering here? how to detect only change of TextInput?
Another way to achieve this would be using the TextInput as a controlled component.
First, create a state with a default value.
Look for changes in that state variable, if it changes from what it was originally, then enable the submit button.
Snack Implementation for the same is here
Approach:
Declare a state variable with a default value
const [name, setName] = React.useState(SOME_DEFAULT_VALUE);
Now for the TextInput, what you can do is
<TextInput placeholder="Enter Name" onChangeText={setName} value={name} />
What this does is, it updates the field with the name variable every time you type something in it.
Now, to look for the changes, what you can do is,
useEffect(() => {
if (name !== SOME_DEFAULT_VALUE) {
setDisableConfirm(false);
} else {
/*
You can disable the button here
If you want user to change the textinput value
atleast once from what it was originally,
for that set disableConfirm to false here
*/
}
}, [name]);
I want to create an observable from a change event that gets fired on a React Native TextInput component. TextInput comes with 2 change props that I'm aware of (onChangeText and onChange). From what I gather, you need to use onChange if you want access to the native event you need to use onChange.
I don't know much about the native event object. I am trying to create an rxjs observable using fromEvent.
First I created a ref in my functional component like this:
const sqftRef = useRef().current
Then I attached this ref to the TextInput component like this:
<TextInput
ref={sqftRef} // attach a ref
label='Sqft'
mode='flat'
textContentType='none'
autoCapitalize='none'
keyboardType='numeric'
autoCorrect={false}
value={String(formValues.sqft)}
dense
underlineColor={colors.colorOffWhite}
onChangeText={(text) => setText(text)}
onChange={e => {
// somehow create an observable from this event ???
}}
style={styles.inputStyles}
theme={inputTheme}
/>
I tried to create an Observable using fromEvent like this but it doesn't work. I get undefined is not an object (evaluating target.addEventListener):
fromEvent(sqftRef, 'onChange').subscribe(value => console.log(value))
I know my approach is all wrong. Hoping someone can point me in the correct direction.
I would emit events you need into a subject, then subscribe to the subject in other parts of your code.
Here's a simple React example that should get you started
function App() {
const textChange = new Subject<string>();
useEffect(() => {
// subscribe to
const subscription = textChange.asObservable().subscribe(console.log)
return () => subscription.unsubscribe()
}, [])
// Emit events with a subject
return <textarea onChange={(e) => {
textChange.next(e.target.value)
}}>
</textarea>
}
render(<App />, document.getElementById('root'));
Check out the example here: https://stackblitz.com/edit/react-ts-akoyfv
I think the problem is with assigning the current directly to the sqftRef. Try to define it without current, but use current when creating the Observable, like the following:
const sqftRef = useRef();
Then create the Observable within useEffect to make sure that the DOM is ready:
useEffect(() => {
fromEvent(sqftRef.current, 'onChange').subscribe((value) =>
console.log(value)
);
});
OK, I was able to figure it out with the help of Amer Yousuf and Alex Fallenstedt.
I did something similar to what Alex suggested, modifying his solution for React Native. One reason his solution wasn't working for me is that it is important to use the useRef hook to prevent the Observable from being re-created on each render. If the observable is recreated (on a re-render) and useEffect doesn't run again, then we won't have an active subscription to the newly (re-created) observable (useEffect never runs again). That's why my call to sqft$.next was originally only being called once (the first time until we re-render).
My solution looks like this:
let sqft$ = useRef(new BehaviorSubject(0)).current
useEffect(() => {
const sub = sqft$.subscribe({
next: (val) => {
// just testing stuff out here
updateForm('sqft', val)
updateForm('lot', val * 2)
}
})
// this is only relevant to my use case
if (activeReport) sqft$.next(activeReport.sqft)
return () => sub.unsubscribe()
}, [activeReport])
and of course I call this in onChangeText:
onChangeText={(text) => {
sqft$.next(text)
}}
So this is working right now. I still feel like there may be a better way using onChange(e => ...stuff). I will leave this question open for a little bit in case anyone can break down how to do this using nativeEvent or explain to me how I can access an event off the TextInput component.
I have a state:
const [userName, setUserName] = useState('');
Next, I have the TextInput as:
<TextInput style={styles.value}
keyboardType={'default'}
placeholder={'Your Name'}
value={userName}
editable={true}
onChangeText={(value) => {
setUserName(value)
}} />
The on a button's onPress event I'm calling this function:
function saveButtonPressed() {
alert("The Value of Name is " + userName);
}
The problem is I can see the vvalue getting updated in the text field, but in the alert I still see '' and if I save tthe coe again the second time it shows the updated value.
The reason they don't show the current value has to do with your functions forming closures. Even if the value of userName changes, it is still a const and since the function where this was updated still has scope in the 1st re-render the updated value wont show. Instead when the scope changes outside the closure thats when you get the updated value in the 2nd re-render.
The first link has solutions for this.
The second link has a bit more information about hooks.
These should answer your question in detail. https://stackoverflow.com/a/58877875/9745240 https://stackoverflow.com/a/54069332/9745240
And a read about closures should help you.
I need to get value from store in redux to TextInput. I know it can be set by value={this.props.Value}. I need to save that value to save later, so I change it by using local state like this
value={this.state.Value}
onChangeText={text => {
this.setState({
Value: text
})
}}
And in constructor of component, I set the value of TextInput to local state
constructor(props) {
super(props);
this.state = {
Value: this.props.Value
}
}
This work fine. But now, that Value is generated on loading this component.
componentDidMount() {
this.props.onLoadView();
}
onLoadView() will dispatch an action that generate new data of Value in store. But constructor function is called before componentDidMount(), that mean component state Value is initial data in store, not the new one. If I change it back to value={this.props.Value} to fix this problem, I can not handle that value to save later.
I know I can change onChangeText to update the value of TextInput to store immediately. But whenever I do not want to save this value, I have to change it back to the initial value. Is there any easier way to solve this problem?
Thank you!
As said in the comment, you can use componentDidUpdate to check your previous props and comparing them to the ones you are using in your component:
componentDidUpdate = () => {
if(this.props.Value !== this.state.Value) this.setState({Value : this.props.Value })
}
As you are storing the previous props.Value in your state you can directly compare the current props with the current state
I have a text input that is empty until a user has change its value only then the state is changed. The problem with React native is that regardless whether there are values on the states it will continue to render. Here is my code so far.
First section the usual react-native code for setting the states
export default class Whereto extends Component<{}> {
constructor(props) {
super(props);
this.state = {
latitude: null,
longitude: null,
location: null,
error: null,
markers:[],
goingto: '',
};
}
Second section of the code is the componentWillMount section, as i understand it is meant to be use so states can be updated before render, here is my try:
componentWillMount(){
navigator.geolocation.getCurrentPosition(
(pos) => {
this.setState({
latitude: pos.coords.latitude,
longitude: pos.coords.longitude,
error: null,
});
//directions api
var apiDirectionskey = '';
const {goingto} = this.state;
fetch('https://maps.googleapis.com/maps/api/directions/json?origin=' + pos.coords.latitude + ',' + pos.coords.longitude + '&destination=' + goingto + '&mode=transit&arrival_time=1391374800&key=' + apiDirectionskey)
.then((resdirections) => resdirections.json())
.then((responseJson3) => {
// noinspection JSAnnotator
if (goingto !== '') {
console.log(responseJson3);
} else {
console.log('no-response');
}
});
Third Section is my render section, which has my text input
render(){
return(
<View style={styles.container}>
<Mainlogo/>
<TextInput style={styles.boxInput} underlineColorAndroid='rgba(0,0,0,0)' placeholder="Going To?"
underlineColorAndroid='transparent'
onChange={(dest) =>this.setState({goingto : dest})}
/>
... truncated from a much larger code..
)
}
}
I did not include a button, just a textbox that changes state called goingto. I have been modifying the code for hours and still not able to get a response or get results from my fetch call because i need the value of my goingto state as a a parameter in order to complete my fetch call. It remains empty or at least i think it is empty after i input change the TextInput. Any pointers on how this is done properly would be great
console.log() results from the set states.
This result comes up first from my fetch section
14:50:46
no-response
This result comes from my text input, only accepting single characters
Object {
"goingto": "N",
}
14:50:55
Object {
"goingto": "O",
}
14:50:55
Object {
"goingto": "T",
}
14:50:55
Object {
"goingto": "T",
}
14:50:56
Object {
"goingto": "I",
Your component will only rerender if its state is updated. In the code you provided you have only 2 setState: first in your componentWillMount and the second in your TextInput. The point is to identify which of those are being called many times so it's causing your component to keep rendering.
I think the best way to debug it is adding a console.log to your setState callback, this way the log will be printed after the setState is finished.
It's important to mention that componentWillMount is a sync method Make sure navigator.geolocation.getCurrentPosition don't need to be async, otherwise, your state can always be empty, since it didn't have enough time to be executed.
If you need getCurrentPosition to be async, you must not call it on componentWillMount.
Let me know if this helped.