Dan
Dan

Reputation: 1246

React Navigation 5 headerRight button function called doesn't get updated states

In the following simplified example, a user updates the label state using the TextInput and then clicks the 'Save' button in the header. In the submit function, when the label state is requested it returns the original value '' rather than the updated value.

What changes need to be made to the navigation headerRight button to fix this issue?

Note: When the Save button is in the render view, everything works as expected, just not when it's in the header.

import React, {useState, useLayoutEffect} from 'react';
import { TouchableWithoutFeedback, View, Text, TextInput } from 'react-native';

export default function EditScreen({navigation}){
  const [label, setLabel] = useState('');

  useLayoutEffect(() => {
      navigation.setOptions({
        headerRight: () => (
          <TouchableWithoutFeedback onPress={submit}>
            <Text>Save</Text>
          </TouchableWithoutFeedback>
        ),
      });
    }, [navigation]);

  const submit = () => {
    //label doesn't return the updated state here
    const data = {label: label}
    fetch(....)
  }

  return(
    <View>
      <TextInput onChangeText={(text) => setLabel(text) } value={label} />  
    </View>
  )

}

Upvotes: 5

Views: 2176

Answers (2)

Dan
Dan

Reputation: 1246

Guruparan's answer is correct for the question, although I wanted to make the solution more usable for screens with many TextInputs.

To achieve that, I added an additional state called saving, which is set to true when Done is clicked. This triggers the useEffect hook to be called and therefore the submit.

export default function EditScreen({navigation}){
  const [label, setLabel] = useState('');
  const [saving, setSaving] = useState(false);

  useLayoutEffect(() => {
      navigation.setOptions({
        headerRight: () => (
          <TouchableWithoutFeedback onPress={() => setSaving(true)}>
            <Text>Done</Text>
          </TouchableWithoutFeedback>
        ),
      });
    }, [navigation]);

    useEffect(() => {
      // Check if saving to avoid calling submit on screen unmounting
      if(saving){
        submit()
      }
    }, [saving]);

    const submit = () => {
      const data = {label: label}
      fetch(....)
    }

    return(
      <View>
        <TextInput onChangeText={(text) => setLabel(text) } value={label} />  
      </View>
    )

}

Upvotes: 5

Guruparan Giritharan
Guruparan Giritharan

Reputation: 16334

Label should be passed as a dependency for the useLayouteffect, Which will make the hook run on changes

  React.useLayoutEffect(() => {
      navigation.setOptions({
        headerRight: () => (
          <TouchableWithoutFeedback onPress={submit}>
            <Text>Save</Text>
          </TouchableWithoutFeedback>
        ),
      });
    }, [navigation,label]);

Upvotes: 9

Related Questions