Skip to content

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

@maximusnikulin

Description

@maximusnikulin

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:

  • when animations is enabled we have extra time for render content of screen. So RNNComponentUIController.viewDidAppear -> RNNReactView.componendDidAppear -> sendEventToJS will be called after js render our screen component.
  • when animations is disabled we don't have extra time, and we called RNNComponentUIController viewDidAppear-> RNNReactView componendDidAppear -> sendEventToJS before js render screen component;

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?

  • I have tested this issue on the latest react-native-navigation release and it still reproduces.

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:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions