最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React Native BackHandler Not Following State Updates - Stack Overflow

programmeradmin3浏览0评论

My backAction function that's used in BackHandler event listener, uses a stage state for conditionals, but doesn't follow the state value updates when stage is updated by other ponents, as it gets stuck on the initial state value. and also return true and false don't do anything.

Expected Behavior:

If back button is pressed check current stage:

  • If stage value is 1 then use return false to trigger back button default behavior and navigate back to the previous screen (with out the need to use navigation.goBack()).
  • If stage value is more that 1 then just decrement the current stage value by -1, and return true to stop the event bubbling.

Current Behavior:

Once back button is pressed:

  • Uses and keeps using on each click stage initial value of 1 even if it's clear that other ponents changed it and used it's new value.
  • return false doesn't even trigger the default behavior of the back button.

Code To Reproduce:

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);
}, []);

Note: I've used the documentation pattern of assigning BackHandler.addEventListener to backHandler, and then using it in the events clean up as backHandler.remove(). didn't work either, but removed it here for the sake of simplicity.

Any help would be much appreciated.

My backAction function that's used in BackHandler event listener, uses a stage state for conditionals, but doesn't follow the state value updates when stage is updated by other ponents, as it gets stuck on the initial state value. and also return true and false don't do anything.

Expected Behavior:

If back button is pressed check current stage:

  • If stage value is 1 then use return false to trigger back button default behavior and navigate back to the previous screen (with out the need to use navigation.goBack()).
  • If stage value is more that 1 then just decrement the current stage value by -1, and return true to stop the event bubbling.

Current Behavior:

Once back button is pressed:

  • Uses and keeps using on each click stage initial value of 1 even if it's clear that other ponents changed it and used it's new value.
  • return false doesn't even trigger the default behavior of the back button.

Code To Reproduce:

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);
}, []);

Note: I've used the documentation pattern of assigning BackHandler.addEventListener to backHandler, and then using it in the events clean up as backHandler.remove(). didn't work either, but removed it here for the sake of simplicity.

Any help would be much appreciated.

Share Improve this question edited Jul 6, 2021 at 0:44 Tom 9,1557 gold badges55 silver badges84 bronze badges asked Jul 5, 2021 at 23:25 MerouaneMerouane 431 silver badge6 bronze badges 0
Add a ment  | 

1 Answer 1

Reset to default 13

First: the return value of your event handler is passed to the JS runtime's event listener system. It doesn't accept promises, it doesn't know how to accept promises. But a promise is a truthy value, which means that your handler is effectively returning true simply because you marked it async.

Second, setState is not asynchronous (i.e. is not declared async), which means there's no need to await it. The code will still run, but there's no benefit. And, since this is the only use of async in the handler, removing this allows you to convert your handler to synchronous code, which is a necessity for responding to the hardwareBackPress correctly.

Third, returning false is the opposite of what you should do if you want the native behavior to occur. You must return true to get default behavior, and false otherwise.

The above paragraph is false. Per this documentation, you had the right idea about return values. (My apologies; my react-native skills are rusty. I've corrected the code sample below to reflect this.)

Third (for realz): you need to tell React Hooks to re-bind the event handler whenever the value of stage changes. The reason this is needed is that the closure will preserve the value of stage from when the function is defined, and it doesn't receive updates. You do this by listing that variable among the hook's dependencies. This is why you're seeing stale values at runtime.

Try something like this instead:

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
发布评论

评论列表(0)

  1. 暂无评论