React Native切换Tabs时TextInput失焦及首次点击键盘闪烁问题
React Native TabView内TextInput焦点异常问题
问题现象
- 切换标签时,TextInput失去焦点,键盘自动收起;
- 首次点击Websites标签下的TextInput时,键盘短暂闪烁后消失,需第二次点击才能正常唤起;
- 仅在从Apps标签切换到Websites标签时出现上述问题,其他标签切换组合无异常。
相关代码
TextInputSelectionScene 子组件
import * as React from 'react' import { useState } from 'react' import { FlatList, StyleSheet, TextInput } from 'react-native' import { T } from '../../../design-system/theme.ts' import { SelectableSirenCard } from '../SelectableSirenCard.tsx' import { SirenType } from '../../../../core/blocklist/blocklist.ts' export function TextInputSelectionScene(props: { onSubmitEditing: (event: { nativeEvent: { text: string } }) => void placeholder: string sirenType: SirenType.WEBSITES | SirenType.KEYWORDS data: string[] toggleSiren: (sirenType: SirenType, sirenId: string) => void isSirenSelected: (sirenType: SirenType, sirenId: string) => boolean }) { const [isFocused, setIsFocused] = useState(true) return ( <> <TextInput style={[ styles.addWebsiteInput, { borderColor: isFocused ? randomColor : T.color.white }, ]} placeholder={props.placeholder} placeholderTextColor={T.color.white} onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} onSubmitEditing={props.onSubmitEditing} /> <FlatList data={props.data} keyExtractor={(item) => item} renderItem={({ item }) => ( <SelectableSirenCard sirenType={props.sirenType} siren={item} onPress={() => props.toggleSiren(props.sirenType, item)} isSelected={props.isSirenSelected(props.sirenType, item)} /> )} /> </> ) } const colors = [ '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#00FFFF', '#FF00FF', ] const randomColor = colors[Math.floor(Math.random() * colors.length)] const styles = StyleSheet.create({ addWebsiteInput: { borderBottomWidth: 2, borderBottomColor: randomColor, padding: T.spacing.small, color: T.color.white, }, })
CreateBlocklistScreen 父组件
import { Dimensions, StyleSheet, Text } from 'react-native' import { NativeStackNavigationProp } from '@react-navigation/native-stack' import { ScreenList } from '../../../navigators/screen-lists/screenLists.ts' import { TabScreens } from '../../../navigators/screen-lists/TabScreens.ts' import { TiedSLinearBackground } from '../../../design-system/components/TiedSLinearBackground.tsx' import { TiedSBlurView } from '../../../design-system/components/TiedSBlurView.tsx' import { T } from '../../../design-system/theme' import { TiedSTextInput } from '../../../design-system/components/TiedSTextInput.tsx' import * as React from 'react' import { useEffect, useState } from 'react' import { installedAppsRepository } from '../../../dependencies.ts' import { InstalledApp } from '../../../../core/installed-app/InstalledApp.ts' import { Route, SceneMap, TabBarProps, TabView } from 'react-native-tab-view' import { TiedSButton } from '../../../design-system/components/TiedSButton.tsx' import { BlocklistsStackScreens } from '../../../navigators/screen-lists/BlocklistsStackScreens.ts' import { ChooseBlockTabBar } from './ChooseBlockTabBar.tsx' import { useDispatch } from 'react-redux' import { createBlocklist } from '../../../../core/blocklist/usecases/create-blocklist.usecase.ts' import { AppDispatch } from '../../../../core/_redux_/createStore.ts' import { Blocklist, Sirens, SirenType, } from '../../../../core/blocklist/blocklist.ts' import { AppsSelectionScene } from './AppsSelectionScene.tsx' import { TextInputSelectionScene } from './TextInputSelectionScene.tsx' type BlocklistScreenProps = { navigation: NativeStackNavigationProp<ScreenList, TabScreens.BLOCKLIST> } export function CreateBlocklistScreen({ navigation, }: Readonly<BlocklistScreenProps>) { const dispatch = useDispatch<AppDispatch>() const [installedApps, setInstalledApps] = useState<InstalledApp[]>([]) const [websites, setWebsites] = useState<string[]>([]) const [keywords, setKeywords] = useState<string[]>([]) const [blocklist, setBlocklist] = useState<Omit<Blocklist, 'id'>>({ name: '', sirens: { android: [], ios: [], windows: [], macos: [], linux: [], websites: [], keywords: [], }, }) const [index, setIndex] = useState(0) const routes = [ { key: 'apps', title: 'Apps' }, { key: 'websites', title: 'Websites' }, { key: 'keywords', title: 'Keywords' }, ] useEffect(() => { installedAppsRepository.getInstalledApps().then((apps) => { setInstalledApps(apps) }) }, []) function toggleSiren(sirenType: keyof Sirens, sirenId: string) { setBlocklist((prevBlocklist) => { const updatedSirens = { ...prevBlocklist.sirens } updatedSirens[sirenType] = updatedSirens[sirenType].includes(sirenId) ? updatedSirens[sirenType].filter( (selectedSiren) => selectedSiren !== sirenId, ) : [...updatedSirens[sirenType], sirenId] return { ...prevBlocklist, sirens: updatedSirens, } }) } function isSirenSelected(sirenType: SirenType, sirenId: string) { return blocklist.sirens[sirenType].includes(sirenId) } const renderScene = SceneMap({ apps: () => ( <AppsSelectionScene data={installedApps} toggleSiren={toggleSiren} isSirenSelected={isSirenSelected} /> ), websites: () => ( <TextInputSelectionScene onSubmitEditing={(event) => setWebsites([...websites, event.nativeEvent.text]) } sirenType={SirenType.WEBSITES} placeholder={'Add websites...'} data={websites} toggleSiren={toggleSiren} isSirenSelected={isSirenSelected} /> ), keywords: () => ( <TextInputSelectionScene onSubmitEditing={(event) => setKeywords([...keywords, event.nativeEvent.text]) } sirenType={SirenType.KEYWORDS} placeholder={'Add keywords...'} data={keywords} toggleSiren={toggleSiren} isSirenSelected={isSirenSelected} /> ), }) return ( <TiedSLinearBackground> <Text style={styles.title}>Name</Text> <TiedSBlurView> <TiedSTextInput placeholder="Blocklist name" onChangeText={(text) => setBlocklist({ ...blocklist, name: text })} /> </TiedSBlurView> <TabView navigationState={{ index, routes }} renderScene={renderScene} lazy={false} onIndexChange={setIndex} initialLayout={{ width: Dimensions.get('window').width }} renderTabBar={(props: TabBarProps<Route>) => ( <ChooseBlockTabBar {...props} /> )} /> <TiedSButton text={'Save Blocklist'} onPress={() => { dispatch(createBlocklist(blocklist)) navigation.navigate(BlocklistsStackScreens.MAIN_BLOCKLIST) }} /> </TiedSLinearBackground> ) } const styles = StyleSheet.create({ title: { fontWeight: T.font.weight.bold, color: T.color.text, fontFamily: T.font.family.primary, fontSize: T.size.small, marginTop: T.spacing.small, marginBottom: T.spacing.small, }, })
已尝试无效的方案
- 设置TabView的
lazy={false},避免切换标签时卸载TextInput组件; - 使用useEffect钩子管理TextInput的焦点状态,尝试在isFocused状态变化时聚焦组件。
内容的提问来源于stack exchange,提问作者A Mehmeto
相关产品推荐
相关产品推荐

