react-nativekeyboardtextinputstack-navigatorcustom-headers

passing text input between custom header using stack navigation


I wanted to connect the SearchMain, SearchHistoryList, and SearchResult screens using the custom header of the stack navigation.

At first, the SearchMain screen appeared, and when the TextInput in the custom header is onFocus, the SearchHistory screen appeared, and the SearchResult screen appeared when the TextInput is onSubmitEditing or when the SearchButton was pressed.

Also, I set the keyboard focus to be maintained when switching screens, and use Keyboard.discuss() when I want to close it.

However, if you enter something at the TextInput in custom header immediately after the screen changes, it will be entered at the TextInput on the previous screen.

I think it's because the three screens don't share a single custom header!! each header is created, and the keyboard focus on the previous screen!!

I want all screens use the same header, or the values of the headers are consistent.

What should I do to solve this problem? I searched variously such as singleton header, static header, and header value consistent, but there is no good solution.

I recently started studying React Native, so I'm not sure about my method, and a completely different approach is also welcome~~

Below is my code, thanks.

const Stack = createStackNavigator();

function NavigationBar({navigation}){
const [query, setQuery] = React.useState('');

function changeSearchQuery(text : string) : void {
    setQuery(text);
}

function deleteSearchQuery() : void {
    setQuery("");
}

return(
    <InputView>
        <BackIcon onPress={() => {navigation.goBack()
                        Keyboard.dismiss()}}>
            <Icon name="arrow-back-ios" size={widthPercentage(24)} color="#666666"/>
        </BackIcon>
        <InputText
            keyboardType="default"
            maxLength={100}
            onChangeText={(str) => setQuery(str)}
            value = {query}
            placeholder="검색어 입력"
            placeholderTextColor="#E9E9E9"
            returnKeyType="search"
            onFocus={() => navigation.navigate("SearchHistory", {changeSearchQuery:changeSearchQuery})}
            onSubmitEditing={() => {navigation.navigate("SearchResult", {changeSearchQuery:changeSearchQuery})
                            Keyboard.dismiss()}}
            allowFontScaling= {false}
        />

        { query.length > 0 && 
            <DeleteIcon onPress={() => deleteSearchQuery()}>
                <Icon name="clear" size={widthPercentage(20)} color="#666666"/>
            </DeleteIcon>
        }
        
        <SearchIcon onPress={() => {navigation.navigate("SearchResult")
                                Keyboard.dismiss()}}>
            <Icon name="search" size={widthPercentage(20)} color="#666666"/>
        </SearchIcon>

        <Line></Line>
    </InputView>
);
}

export const Search = () =>{
return (
    <Stack.Navigator initialRouteName="SearchMain" keyboardHandlingEnabled={false} screenOptions={{header:props => <NavigationBar navigation={props.navigation}/>}}>
        <Stack.Screen name="SearchMain" component={SearchMain} options={{animationEnabled: false}}/>
        <Stack.Screen name="SearchHistory" component={SearchHistory} options={{animationEnabled: false}}/>
        <Stack.Screen name="SearchResult" component={SearchResult} options={{animationEnabled: false}}/>
        <Stack.Screen name="SearchFilter" component={SearchFilter} options={{headerShown: false}}/>
    </Stack.Navigator>
);
}

export default Search;

Solution

  • It's not a good pattern to set the state inside the header, instead you should centralized the state in your component and then use navigation.setOption to pass functions/state value.

    In this way your header will look like this:

    function NavigationBar({
        query,
        onFocus,
        onChangeText,
        onPressBackIcon,
        onSubmitEditing,
        onDeleteSearch,
        onSearch
        }){
    
    return(
        <InputView>
            <BackIcon onPress={onPressBackIcon}>
                <Icon name="arrow-back-ios" size={widthPercentage(24)} color="#666666"/>
            </BackIcon>
            <InputText
                keyboardType="default"
                maxLength={100}
                onChangeText={onChangeText}
                value = {query}
                placeholder="검색어 입력"
                placeholderTextColor="#E9E9E9"
                returnKeyType="search"
                onFocus={onFocus}
                onSubmitEditing={onSubmitEditing}
                allowFontScaling= {false}
            />
    
            { query.length > 0 && 
                <DeleteIcon onPress={onDeleteSearch}>
                    <Icon name="clear" size={widthPercentage(20)} color="#666666"/>
                </DeleteIcon>
            }
            
            <SearchIcon onPress={onSearch}>
                <Icon name="search" size={widthPercentage(20)} color="#666666"/>
            </SearchIcon>
    
            <Line></Line>
        </InputView>
    );
    }
    

    Than you will have in SearchMainScreen your state, your functions and your custom header:

    // remember headerMode='none' in screen option
    const SearchMainScreen = () => {
    const [query, setQuery] = React.useState('');
    const onFocus = () => {}
    ...
    
    return (
    <View>
    <NavigationBar
      onFocus={onFocus}
      query={query}
      ...
    />
    {
          (isFocus && !query) ? 
          <SearchHistoryComponent/> : 
          (isFocus && query) ? 
          <SearchResultComponent/> :
          <List/>
    }
    </View>
    )
    }