wix / react-native-navigation

A complete native navigation solution for React Native
https://wix.github.io/react-native-navigation/
MIT License
13.01k stars 2.68k forks source link

Event RNN.ComponentDidAppear called before render screen component when animations.showModal.enabled = false #7843

Open maximusnikulin opened 4 months ago

maximusnikulin commented 4 months ago

What happened?

Hi there, I have not clear behaviour when I disabled animation on showModal. I want to call function when my screen appears. In IOS Stack "Appears" means when I go to this screen, and back to this screen my callback fn must be called. For that I wrote little hook:

export function useNavigationComponentDidAppear(handler: (ev: ComponentDidAppearEvent) => void, cId?: string) {
  const ctx = React.useContext(NavigationContext);
  const componentId = cId || ctx?.componentId;

  useLayoutEffect(() => {
    console.log('[HOOK LAYOUT EFFECT]');
    const subscription = Navigation.events().registerComponentDidAppearListener((ev) => {
      if (componentId === ev.componentId) handler(ev);
    });

    return () => subscription.remove();
  }, [handler, cId]);
}

Use it in my experiment screen:

export const SettingsView = ({ componentId }) => {
  useNavigationComponentDidAppear(
    React.useCallback(() => {
      console.log('DID APPEAR SETTINGS VIEW');
    }, []),
    componentId,
  );

  return <View><Text>Settings screen</Text></View>
}

Call showModal:

Navigation.showModal({
      component: {
          name: 'SETTINGS_VIEW',
      }
});

So when animations is on, everything is fine. I have logs:

 LOG  HOOK LAYOUT EFFECT
 LOG  RNN.appearedComponent name=SETTINGS_VIEW id=SETTINGS_VIEW
 LOG  DID APPEAR SETTINGS VIEW

But when I disable animations on modals

   Navigation.setDefaultOptions({
        animations: {
          showModal: {
            enter: {
              enabled: false,
            },
          },
        }})

I called event before layout effect

 LOG  RNN.appearedComponent name=SETTINGS_VIEW id=SETTINGS_VIEW
 LOG  HOOK LAYOUT EFFECT

I got it that get RNN.appearedComponent event before I subscribed on it, so my callback wasn't called. I started investigate why that happens.

I guess in that point we have race condition:

We have RCTContentDidAppearNotification tells us that all subview of RCTRootContentView was show on screen. I know that we have flag waitForRender, but it waits for that event and only after that start open modal, so we can have little delay between tap and show modal. Instead we should send RNN.componentDidAppear event to JS when we get RCTContentDidAppearNotification

I did smth like this:

@implementation RNNReactView {
    BOOL _isAppeared;
    BOOL _isContentAppeared;
}
....
   - (void)contentDidAppear:(NSNotification *)notification {
        RNNReactView *appearedView = notification.object;
        if ([appearedView.appProperties[@"componentId"] isEqual:self.componentId]) {
            [self reactViewReady];
            _isContentAppeared = true;
             // send event RNN.ComponentDidAppear to js
            [self componentDidAppear];
        }
    }

    - (void)componentDidAppear {
       // don't call until I get event RCTContentDidAppearNotification
        if (!_isContentAppeared) return;

        if (!_isAppeared) {
            [_eventEmitter sendComponentDidAppear:self.componentId
                                    componentName:self.moduleName
                                    componentType:self.componentType];
        }

        _isAppeared = YES;
    }

After that I get that log

 LOG  HOOK LAYOUT EFFECT
 LOG  RNN.appearedComponent name=SETTINGS_VIEW id=SETTINGS_VIEW
 LOG  DID APPEAR SETTINGS VIEW

I want to understand is this right behavior by default ? Now I use waitForRender: true with disabled showModal animations, and it works as temporary solution I can create repo with example. Sorry for many letters =)

What was the expected behaviour?

When animations disabled I want get event RNN.componentDidAppear in my component

Was it tested on latest react-native-navigation?

Help us reproduce this issue!

No response

In what environment did this happen?

React Native Navigation version: React Native version: Has Fabric (React Native's new rendering system) enabled: (yes/no) Node version: Device model: iOS version: