Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
1 task done
maximusnikulin opened this issue Feb 8, 2024 · 0 comments

Comments

@maximusnikulin
Copy link

maximusnikulin commented Feb 8, 2024

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:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant