skip to Main Content

I have a global component called <MyGlobalComponent/> that resides in <App/>.
What I am trying to achieve is that I want to build a global function showGlobalComponent() that can be called from any other component, one which sets its visibility and the content rendered inside it (which is another component).

// MyGlobalComponent.tsx

export type GlobalComponentProps {
  Component: React.FC
  isVisible: boolean
};

export function showGlobalComponent ({Component, isVisible}: GlobalComponentProps) {
  // ??? What do I do here ??? 
  // Supposedly I'd set isVisible & ComponentToRender state variables here, but I can't, I'm outside the function
}

     function MyGlobalComponent() {
           const [ComponentToRender, setComponentToRender] = useState<React.FC | null>(null);
           const [isVisible, setIsVisible] = useState<boolean>(false)
          return (
            <View style={APPSTYLE.myComponentStyle} />
              {
                isVisible &&
                <>
                 ComponentToRender
                </>
              } 
            </View>
          )

Here is the example of how I’d call it

   // AnotherComponent.tsx
    
         function AnotherComponent() {
          
         useEffect(() => {
           showGlobalComponent({isGlobalComponentVisible: true, Component => <TertiaryComponent myProp="asds"/>})
          }, [])

              }

I thought about using Redux and only memorizing a string associated with the component I want to render inside ` (because you cannot memorize non-serializable stuff with Redux). However, if I want to pass a prop to it like I did above to TertiaryComponent, that becomes quite impossible.

A solution I thought of (which I’m in doubt about) is using DeviceEventEmitter from ‘react-native’ to emit an event and then listening to it inside an useEffect in <MyGlobalComponent/>

// MyGlobalComponent.tsx
 export function showGlobalComponent ({Component, isVisible}: GlobalComponentProps) {
      DeviceEventEmitter.emit("myEventName", Component, isVisible);
    }
// ...
useEffect(() => {
    const myEvent = DeviceEventEmitter.addListener(
      "myEventName",
      ({Component, isVisible}: GlobalComponentProps) => {
        setIsVisible(isVisible)
        setComponentToRender(Component)
   
      }
    );
    return () => {
      myEvent.remove();
    };
  }, []);

However, the behaviour of this is inconsistent and this doesn’t seem the right way to do it. Besides, DeviceEventEmitter is very poorly documented.
What’s a better way to achieve it?

2

Answers


  1. You need to React.forwardRef to your global component first. Then you can assign show/hide functions using React.useImperativeHandle hook.

    https://github.com/calintamas/react-native-toast-message/blob/main/src/Toast.tsx

    Check this implementation, it might help you solve the issue.

    Login or Signup to reply.
  2. you can use references and reference manipulation hooks such as useImperativeHandle for this job.

    I prepared an example because I thought it might guide you. An additional way to communicate between components is to use global references.

    These methods are generally used in my own projects, such as Modal, Toast, etc., which will be used throughout the application. I use it for elements. Here is the example;

    import React, {useImperativeHandle, useState} from 'react';
    import {Button, View} from 'react-native';
    
    const GlobalComponentRef = React.createRef<GlobalComponentRef>();
    
    const App = () => {
      return (
        <View>
          <GlobalComponent ref={GlobalComponentRef} />
          <AnotherComponent />
        </View>
      );
    };
    
    type GlobalComponentProps = {Component?: React.FC; visible?: boolean};
    type GlobalComponentRef = {
      state: () => GlobalComponentProps;
      setComponent: (component: React.FC) => void;
      setVisible: (bool: boolean) => void;
    };
    
    const GlobalComponent = React.forwardRef<
      GlobalComponentRef,
      GlobalComponentProps
    >(
      (
        {Component = React.Fragment, visible = false}: GlobalComponentProps,
        ref,
      ) => {
        const [_Component, setComponent] = useState<React.FC>(Component);
        const [_visible, setVisible] = useState<boolean>(visible);
    
        useImperativeHandle(
          ref,
          () => {
            return {
              state: () => ({visible: _visible, Component: _Component}),
              setComponent: (component: React.FC) => setComponent(component),
              setVisible: (bool: boolean) => setVisible(bool),
            };
          },
          [_Component, _visible],
        );
    
        return <View>{visible && <>ComponentToRender</>}</View>;
      },
    );
    
    const AnotherComponent = () => {
      const onPress = () => {
        const {visible} = GlobalComponentRef.current?.state() || {};
        GlobalComponentRef.current?.setVisible(!visible);
      };
    
      return (
        <View>
          <Button title={'Click Me'} onPress={onPress} />
        </View>
      );
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search