Home > OS >  useEffect not firing on state update
useEffect not firing on state update

Time:01-27

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);
}}
  • Related