GithubHelp home page GithubHelp logo

Comments (2)

brentvatne avatar brentvatne commented on July 18, 2024

Today, when we add a will/didFocus listener on a screen, if that screen is currently focused, the listener will fire.

can you provide a snack that demonstrates clearly what is happening here?

from rfcs.

slorber avatar slorber commented on July 18, 2024

Hi @brentvatne

TLDR

I was not totally correct in all my assumptions above.
The following seems to apply to willFocus and didFocus

Infinite loop.

When using declarative event comp or hooks, it's simpler to unsub/resub, but this can easily lead to the following:

  componentDidMount() {
    this.props.navigation.addListener('willFocus', () => {
      this.incrementCounter();
      this.props.navigation.addListener('willFocus', () => {
        this.incrementCounter();
      });
    });
  }

Counter = 2, but I think in such case it should be counter = 1, which would solve the infinite loop problem. Redux had a similar issue too.

Breaking change would be small and may be considered as a bug.

Do we really want willFocus on mount?

When mounting the initial navigation, on the initial route names, without any animation etc, do we really want focus events to fire? What are the usecases for this? This complicates the usecase where you want to refresh api data on focus, because it might lead to a double fetch on mount.

More important breaking change, but with clear migration path possible.

Details

Infinite loop.

Here is a snack that shows the infinite loop problem the current solution can actually trigger: https://snack.expo.io/@slorber/focus-event

The original issue reporting this is react-navigation/react-navigation#5058

This loop happens because:

  • The listener triggers a state change
  • We unsub/resub on every update
  • The resub triggers the listener

My snack may feel convoluted and the unsub/resub unnecessary, but when using declarative approachs like or hooks, particularly when providing closures as listeners, it's important to ensure the last closure provided is run because a closure capture external variables at creating time, and those variables may change over time. Unsub/resub permits to easily ensure the last closure is run.

Actually I think my initial description of the problem is not totally correct. It seems unsub/resub happening inside a "didFocus" is actually the problem. The button in the snack does show that when the screen is already focused, and we add a listener, this listener will actually not be fired even if the screen is focused (where I was wrong).

As far as I remember, Redux had this issue too and solved it by creating a listener array copy before iterating on them, so that on resub, the newly added listener won't be executed before next focus. Doing something similar can make sense here to ensure that we don't encounter any infinite loop.

The following should only print 1 imho, currently it prints 2: https://snack.expo.io/@slorber/focus-event-2

Do we really want willFocus on mount?

This would solve the infinite loop issue, but would not solve the issue where the user might not necessarily want this listener to fire on mount (because it may be wired to a "refresh api data" method)

For me the main usecase to willFocus was mostly to track focused screen before, but we have better alternatives now (withNavigationFocus, navigation.isFocused()...). The main usecase that remains for me is to refresh api data on screen focus. But generally the tools we use already do the fetching on mount (like Apollo) so having this event firing will trigger a "double fetch" on mount. Do we really want this event to fire when the initial navigator screen is mounting?

I don't know how users are using this listener, maybe my assumptions are incorrect, and it would be a larger breaking change.

from rfcs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.