React Native BackHandler 不遵循状态更新

Mer*_*ane 2 javascript reactjs react-native expo

我在事件侦听器backAction中使用的函数使用条件状态,但在其他组件更新时不遵循状态值更新,因为它卡在初始状态值上。并且返回 true 和 false 不做任何事情。BackHandlerstagestage

预期行为:

如果按下后退按钮,检查当前阶段:

  • 如果阶段值为 1,则使用return false触发后退按钮默认行为并导航回上一屏幕(无需使用navigation.goBack())。
  • 如果阶段值大于 1,则只需将当前阶段值减 -1,并return true停止事件冒泡。

当前行为:

按下后退按钮后:

  • 在每次点击时使用并继续使用stage初始值 1,即使很明显其他组件更改了它并使用了它的新值。
  • return false甚至不会触发后退按钮的默认行为。

重现代码:

const [stage, setStage] = useState(1);

const backAction = async () => {
  console.log(stage); // the bug: stage is always 1

  if (stage === 1) {
    console.log("Going back to previous screen!");
    return false;
  } else if (stage > 1) {
    console.log("Setting stage back with -1");
    await setStage((prevState) => prevState - 1);
    return true;
  }

  return false;
};

useEffect(() => {
  BackHandler.addEventListener("hardwareBackPress", backAction);

  return () => BackHandler.removeEventListener("hardwareBackPress", backAction);
}, []);
Run Code Online (Sandbox Code Playgroud)

注意:BackHandler.addEventListener我使用了分配给的文档模式backHandler,然后在事件清理中使用它作为backHandler.remove()。也不起作用,但为了简单起见将其删除。

任何帮助将非常感激。

Tom*_*Tom 13

首先:事件处理程序的返回值被传递到 JS 运行时的事件侦听器系统。它不接受承诺,它不知道如何接受承诺。但 Promise 是一个真实值,这意味着您的处理程序实际上true只是因为您标记了它而返回async

其次,setState不是异步的(即未声明async),这意味着不需要await它。代码仍然会运行,但没有任何好处。而且,由于这是处理程序中的唯一用途async,因此删除它可以让您将处理程序转换为同步代码,这是正确响应的必要条件hardwareBackPress

第三,false如果您希望发生本机行为,则返回与您应该做的相反。您必须返回true才能获得默认行为,false否则。

上面这段话是错误的。根据本文档,您对返回值有正确的想法。(抱歉;我的反应原生技能很生疏。我已经更正了下面的代码示例以反映这一点。)

第三(对于realz):您需要告诉React Hooks在值发生变化时重新绑定事件处理程序stage。需要这样做的原因是闭包将保留定义函数时 from 的值stage,并且它不会接收更新。您可以通过在钩子的依赖项中列出该变量来完成此操作。这就是为什么您在运行时看到过时的值的原因。

尝试这样的事情:

const [stage, setStage] = useState(1);

const backAction = () => {
  console.log(stage);

  if (stage === 1) {
    console.log("Going back to previous screen!");
    return false;
  } else if (stage > 1) {
    console.log("Setting stage back with -1");
    setStage((prevState) => prevState - 1);
    return true;
  }

  return false;
};

useEffect(() => {
  BackHandler.addEventListener("hardwareBackPress", backAction);

  return () => BackHandler.removeEventListener("hardwareBackPress", backAction);
}, [ stage ]);  // <-- this is the part you're missing
Run Code Online (Sandbox Code Playgroud)