I have a big form in a ScrollView with keyBoardShouldPersistTaps set to "handled", and are saving the the values entered into Global State when onBlur is called in a TextInput (the onChangeText is saved to local state within each component to be more performant).
The problem comes when my users are submitting the form, if they click on the submit button while still having a textinput in focus (with values in it) the submit call is run without the values from the textinput that was in focus when the submit button was pressed.
All other values in all other textinputs are submitted correctly, as the state has had time to update correctly.
How can I make sure that the onBlur and useState (which is async I know) is updated before the submit function is called?
Simplified code to explain my challenge (expo snack):
import * as React from 'react';
import { ScrollView, StyleSheet, Button, Alert, TextInput, Keyboard } from 'react-native';
import Constants from 'expo-constants';
export default function App() {
const [globalValue, setGlobalValue] = React.useState('Useless Placeholder');
const [localValue, setLocalValue] = React.useState(globalValue);
return (
<ScrollView keyboardShouldPersistTaps={"handled"} style={styles.container}>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={text => setLocalValue(text)}
value={localValue}
onBlur={() => setGlobalValue(localValue)}
/>
<Button
title="Press me"
onPress={() => {Keyboard.dismiss(); Alert.alert(globalValue);}}
/>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
Steps to reproduce my problem:
Steps to reproduce a working "submission":
OLD CODE: Can't you do as below or you have to use another state variable called 'text'?
export default function App() {
const placeHolder = "Useless Placeholder";
const [value, onChangeText] = React.useState(placeHolder);
return (
<ScrollView keyboardShouldPersistTaps={"handled"} style={styles.container}>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={text => onChangeText(text)}
value={value}
onBlur={() => {
onChangeText(value || placeHolder);
}}
/>
<Button
title="Press me"
onPress={() => {Keyboard.dismiss(); Alert.alert(value);}}
/>
</ScrollView>
);
}
Updated code: This is the only way I can see as of now
export default function App() {
const placeHolder = 'Useless Placeholder';
const [globalValue, setGlobalValue] = React.useState(placeHolder);
const [localValue, setLocalValue] = React.useState(globalValue);
useEffect(() => {
Keyboard.dismiss();
if(placeHolder !== globalValue) {
Alert.alert(globalValue);
}
}, [globalValue]);
return (
<ScrollView keyboardShouldPersistTaps={"handled"} style={styles.container}>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={text => setLocalValue(text)}
value={localValue}
/>
<Button
title="Press me"
onPress={() => {setGlobalValue(localValue)}}
/>
</ScrollView>
);
}