I am updating some state with useState()
in my react native component. Once I have that state set I want to save the details to the server, so I have set that up in an useEffect()
hook. To be clear, the setting of the state IS working, and I see the value print to the screen.
However, what I'm noticing is, even though I've set note
as a dependency in the dependency array of the useEffect()
hook, the function never fires when the state updates. What am I missing here?
const [note, setNote] = useState('');
const dispatch = useDispatch();
useEffect(() => {
if (note) {
console.log('updating note...');
dispatch(updateNote(props.client.id, note));
}
}, [note]);
FYI, I am updating the state inside a TextInput, like so (I had to use onBlur to avoid the issue of react loosing focus on the first character type because I am passing a component within screenOptions
of a TabNavigator):
<TextInput
key='note'
style={{
color: '#fff',
fontSize: 16,
width: '100%',
textAlign: 'center',
}}
placeholder='Tap here to share something...'
placeholderTextColor={styles.colors.textPlaceholder}
maxLength={50}
onBlur={(text) => setNote(text)}
defaultValue={note || props?.client?.note?.value}
/>
As I mentioned, this has been a tricky situation because I had to get around react's loss of focus when I rely on onChangeText()
or onChange()
. I am passing in a CustomHeader
- which is a function inside the parent, to a TabNavigator within screenOptions
like so:
screenOptions={{
headerShown: true,
headerStyle: {
backgroundColor: Colors.primary,
elevation: 0,
shadowOpacity: 0,
shadowColor: 'transparent',
height: 170,
},
key: 'patient-tab',
headerShadowVisible: false,
tabBarStyle: { backgroundColor: Colors.primary },
headerTintColor: Colors.light,
headerTitle: (props) => <CustomHeader {...props} />, // Passed in here
tabBarActiveTintColor: Colors.light,
tabBarInactiveTintColor: Colors.lightInactive,
tabBarActiveBackgroundColor: Colors.primary,
tabBarInactiveBackgroundColor: Colors.primary,
}}
The full code looks like this:
export const ClientBottomTabNavigator = (props) => {
const [note, setNote] = useState('');
const dispatch = useDispatch();
useEffect(() => {
if (props.client.id && note) {
console.log('updating note...');
dispatch(updateNote(props.client.id, note));
}
}, [props.client.id, note]);
const CustomHeader = () => {
return (
<View>
<View style={{ width: '100%', flexDirection: 'row', justifyContent: 'space-between', marginBottom: 6 }}>
<Feather
name='chevron-left'
size={24}
onPress={() => props.navigation.goBack()}
color={styles.colors.textInverse}
style={{ justifySelf: 'flex-start', alignSelf: 'flex-start', width: '32%', marginBottom: 6 }}
/>
<Text
style={{
color: '#fff',
fontSize: 18,
alignSelf: 'center',
justifySelf: 'center',
fontWeight: 'bold',
}}
>
{props?.client?.firstName} {props?.client?.lastName}
</Text>
<Feather
name='' // Leave this blank
style={{ justifySelf: 'flex-end', alignSelf: 'flex-end', width: '32%' }}
/>
</View>
<View style={{ alignItems: 'center', marginBottom: 6 }}>
<Text style={{ color: '#fff', fontSize: 18, marginBottom: 6 }}>
{convertDiscipline(props?.client?.discipline)}
</Text>
<View>
<TextInput
key='note'
style={{
color: '#fff',
fontSize: 16,
width: '100%',
textAlign: 'center',
}}
placeholder='Tap here to share something…’
placeholderTextColor={styles.colors.textPlaceholder}
maxLength={50}
onBlur={(text) => setNote(text)}
defaultValue={note || props?.client?.note?.value}
/>
</View>
</View>
</View>
);
};
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator
screenOptions={{
headerShown: true,
headerStyle: {
backgroundColor: Colors.primary,
elevation: 0,
shadowOpacity: 0,
shadowColor: 'transparent',
height: 170,
},
key: 'client-tab',
headerShadowVisible: false,
tabBarStyle: { backgroundColor: Colors.primary },
headerTintColor: Colors.light,
headerTitle: (props) => <CustomHeader {...props} />, // Passed in here
tabBarActiveTintColor: Colors.light,
tabBarInactiveTintColor: Colors.lightInactive,
tabBarActiveBackgroundColor: Colors.primary,
tabBarInactiveBackgroundColor: Colors.primary,
}}
initialRouteName={'Visits'}
>
<Tab.Screen
name='Visits'
component={VisitsTab}
options={{
tabBarLabel: 'VISITS',
tabBarIcon: ({ color, size }) => <FontAwesome name='street-view' color={color} size={size} />,
}}
/>
<Tab.Screen
name='Chart'
component={ChartTab}
options={{
tabBarLabel: 'CHARTS',
tabBarIcon: ({ color, size }) => <FontAwesome name='id-badge' color={color} size={size} />,
}}
/>
<Tab.Screen
name='Goals'
component={GoalsTab}
options={{
tabBarLabel: 'EDIT GOALS',
tabBarIcon: ({ color, size }) => <FontAwesome name='trophy' color={color} size={size} />,
}}
/>
</Tab.Navigator>
);
};
CodePudding user response:
onBlur={(text) => setNote(text)}
Here's the problem. The handler's argument isn't the value of input, it's a synthetic React event. Should be
onBlur={(event => setNote(event.target.value)}
Working example: https://stackblitz.com/edit/react-ts-wqr9dg?file=App.tsx
CodePudding user response:
See here https://playcode.io/1106437, it works, but you have onBlur, you need to blur to trigger the action, maybe you need to use onChangeText insted.
CodePudding user response:
I see you are not using the onChangeText which causing re-rendering and then losses focus from TextInput component.
using onBlur method will never give the entered value which is clearly mentioned on RN TextInput Docs.
Also they have mentioned workaround for the same that we can use onEndEditing method in order to get latest value from text input for the same behaviour.
Please have a look at the Official Doc.
Also I am adding usecase of the method
Ex.
onEndEditing={e => {
setNote(e.nativeEvent.text);
}}