You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Stack Navigation键盘状态异常问题咨询:返回页面时键盘状态不一致

React Native Stack Navigation键盘状态异常问题咨询:返回页面时键盘状态不一致

嘿,我来帮你捋捋这个奇怪的键盘问题~ 先仔细看了你的代码和描述,咱们一步步拆解原因,再给你靠谱的解决方案。

为啥会出现这种前后不一致的情况?

先拆解你的代码逻辑和React Navigation的行为:

  1. 第一次返回键盘弹出的原因:你在Foo页面给导航加了transitionEnd监听,每次页面切换完成后都会自动让输入框聚焦。当你第一次从Bar返回Foo时,Bar的输入框失去焦点键盘隐藏,但Foo的transitionEnd立刻触发了输入框聚焦,所以键盘又弹出来了,看起来像是“键盘保留”了。
  2. 第二次取消返回再返回键盘不弹的原因:这和导航中断时的焦点状态混乱有关。当你触发返回又取消时,transitionEnd其实已经触发过一次,Foo的输入框短暂聚焦,但随后你回到Bar,Bar的输入框(因为autoFocus或者页面重新获得焦点)又抢回了焦点,键盘再次弹出。等你再返回Foo时,transitionEnd再次触发focus(),但此时输入框的焦点状态因为之前的来回切换已经乱了——系统可能认为键盘刚被隐藏,或者输入框“看起来”已经聚焦但实际没同步,导致focus()没触发键盘弹出。

给你几个可行的修复方案

方案1:精准控制transitionEnd的触发场景

我们可以判断只有从Bar返回Foo时才触发聚焦,同时检查输入框当前是否真的没聚焦,避免重复操作:

useEffect(() => {
  const unsubscribe = navigation.addListener("transitionEnd", (e) => {
    // 确认当前是回到Foo页面,且上一个页面是Bar
    const currentRoute = e.data.state.routes[e.data.state.index];
    const lastRoute = e.data.lastState?.routes[e.data.lastState.index];
    if (currentRoute.name === "Foo" && lastRoute?.name === "Bar") {
      // 只有输入框没聚焦时才触发focus
      if (inputRef.current?.isFocused() === false) {
        inputRef.current?.focus();
      }
    }
  });

  return unsubscribe;
}, [navigation]);

方案2:用beforeRemove事件替代transitionEnd,时机更准确

在Bar页面监听返回前的事件,提前让Foo的输入框聚焦,这样能避免导航状态混乱带来的问题。这里可以用Context来传递输入框的引用:

// 先在全局创建一个Context用来存Foo的输入框引用
const InputRefContext = createContext<React.RefObject<TextInput> | null>(null);

// 在App组件中包裹Context
export default function App() {
  const fooInputRef = useRef<TextInput>(null);
  return (
    <InputRefContext.Provider value={fooInputRef}>
      <NavigationContainer>
        <Stack.Navigator screenOptions={{ headerShown: false }}>
          <Stack.Screen name="Foo" component={Foo} />
          <Stack.Screen name="Bar" component={Bar} />
        </Stack.Navigator>
      </NavigationContainer>
    </InputRefContext.Provider>
  );
}

// 在Foo组件中使用这个Context的ref
function Foo({ navigation }: { navigation: NativeStackNavigationProp<RootStackParamList> }) {
  const inputRef = useContext(InputRefContext);
  const [input, setInput] = useState("");

  useEffect(
    () => navigation.addListener("transitionEnd", () => inputRef?.current?.focus()),
    [navigation, inputRef]
  );

  const onChangeInput = (text: string) => {
    setInput(text);
    if (text.length == 1) navigation.navigate("Bar");
  };

  return (
    <TextInput
      ref={inputRef?.current}
      value={input}
      onChangeText={onChangeInput}
      inputMode="tel"
      autoFocus
    />
  );
}

// 在Bar组件中监听beforeRemove事件,返回前让Foo的输入框聚焦
function Bar({ navigation }: { navigation: NativeStackNavigationProp<RootStackParamList> }) {
  const inputRef = useContext(InputRefContext);
  useEffect(() => {
    const unsubscribe = navigation.addListener("beforeRemove", () => {
      inputRef?.current?.focus();
    });
    return unsubscribe;
  }, [navigation, inputRef]);

  return <TextInput inputMode="tel" autoFocus />;
}

方案3:去掉autoFocus,手动控制焦点

Bar页面的autoFocus可能会在导航中断时抢焦点,导致状态混乱。可以改成组件挂载时手动聚焦:

function Bar() {
  const barInputRef = useRef<TextInput>(null);
  
  useEffect(() => {
    barInputRef.current?.focus();
  }, []);

  return <TextInput ref={barInputRef} inputMode="tel" />;
}

总结一下

第一次返回键盘弹出是因为transitionEnd的聚焦逻辑生效了,第二次是因为导航中断导致焦点状态同步出问题,让聚焦操作没触发键盘。上面的方案都能解决这个问题,你可以根据自己的代码结构选最适合的~

备注:内容来源于stack exchange,提问作者beardeadclown

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 10:47:50