jeremybarbet / react-native-portalize

The simplest way to render anything on top of the rest.
MIT License
335 stars 22 forks source link

Ref’s becoming null #7

Closed benjaminreid closed 4 years ago

benjaminreid commented 4 years ago

Hello 👋

I ran into an issue today using react-navigation, using a stack navigator and having different <Portal>s in each tab.

Each <Portal> would have a ref to a Modalize component as it’s child component and due to some behaviour of switching tabs, putting the app into the background and back to the foreground, the ref’s to those Modalize components would be lost (resulting them being set back to null).

I’ve checked out react-native-portalize locally and replaced the mount function in Host.tsx with...

  const mount = (children: React.ReactNode): number => {
    const key = nextKey++;

    if (managerRef.current) {
      // @ts-ignore
      const mountKey = children.key && children ? children.key : key;
      managerRef.current.mount(mountKey, children);
    } else {
      // @ts-ignore
      const mountKey = children.key && children ? children.key : key;
      queue.push({ type: 'mount', key: mountKey, children });
    }

    return key;
  };

This is essentially allowing the component’s rendered inside of <Portal> to mount with their own keys.

It seems to be working, besides the TS issue which I haven’t looked into yet and just ignored for now (accessing children.key) and not sure what other parts of the codebase this would impact.

@jeremybarbet What do you think of this? Basically the numerical key that’s generated in some scenario seems to be clashing. I ended up logging the the portals array in Manage.tsx and it ended up with duplicate keys [{ key: 0, ... }, { key: 0 ... }]`, thus trying to specify unique keys from the components themselves.

Again, awesome work with these components, any help would be massively appreciated. Happy to draft up a PR if you’d like.

benjaminreid commented 4 years ago

Although after some testing, the duplication is still happening, just with the referenced keys... Will try diagnose further.

benjaminreid commented 4 years ago

Just keeping this here for reference, maybe nearer the issue. It’s pretty easy to replicate getting the state of portals in the Manager to look like so...

Screenshot 2020-08-10 at 11 22 48

Then presuming in the update method... that check for the key to update it can potential be either component.

update(key: number, children: React.ReactNode): void {
  setPortals(prev =>
    prev.map(item => {
      if (item.key === key) {
        return { ...item, children };
      }

      return item;
    }),
  );
}
jeremybarbet commented 4 years ago

Release in 1.0.5. Thank you for your help @benjaminreid 🙌🏻

michavie commented 3 years ago

Hey @jeremybarbet & @benjaminreid ,

I experience this issue when the portalized component gets unmounted and remounted again by it's parent.

Here's a visualization of what that looks like:

const Parent = () => {
// IF THIS 'someCondition' DYNAMICALLY CHANGES, THE REF IS LOST WITH THE PORTAL, not using portals it works correctly (apart from styling issues)
  if (someCondition) {
    return <SomethingElse />
  }

  return (
    ...
    ...
    <Modal open={someState} onClose={() => setSomeState(false)}>Some content</Modal>
  )
}

const Modal = ({ open, onClose: handleClose, children }: IProps) => {
  // THIS REF IS LOST AFTER 'someCondition' IN PARENT COMPONENT INVERTS TWICE
  const modalRef = useRef<Modalize>(null)

  useEffect(() => {
    if (open) modalRef.current?.open()
  }, [open, modalRef])

  return (
    <Portal>
      <Modalize ref={modalRef} snapPoint={400} adjustToContentHeight onClose={handleClose}>
        <View style={styles.content}>{children}</View>
      </Modalize>
    </Portal>
  )
}

Do you have any ideas on how this can be solved?