Home > database >  Is it possible to expose a function defined within a React function component to be called in other
Is it possible to expose a function defined within a React function component to be called in other

Time:03-18

I'm refactoring some old code for an alert widget and am abstracting it into its own component that uses DOM portals and conditional rendering. I want to keep as much of the work inside of this component as I possibly can, so ideally I'd love to be able to expose the Alert component itself as well as a function defined inside of that component triggers the render state and style animations so that no outside state management is required. Something like this is what I'm looking to do:

import Alert, { renderAlert } from '../Alert'

const CopyButton = () => (
  <>
    <Alert text="Text copied!" />
    <button onClick={() => renderAlert()}>Copy Your Text</button>
  </>
)

Here's what I currently have for the Alert component - right now it takes in a state variable from outside that just flips when the button is clicked and triggers the useEffect inside of the Alert to trigger the renderAlert function. I'd love to just expose renderAlert directly from the component so I can call it without the additional state variable like above.

const Alert = ({ label, color, stateTrigger }) => {
  const { Alert__Container, Alert, open } = styles;

  const [alertVisible, setAlertVisible] = useState<boolean>(false);
  const [alertRendered, setAlertRendered] = useState<boolean>(false);

  const portalElement = document.getElementById('portal');

  const renderAlert = (): void => {
    setAlertRendered(false);
    setAlertVisible(false);

    setTimeout(() => {
      setAlertVisible(true);
    }, 5);
    setAlertRendered(true);

    setTimeout(() => {
      setTimeout(() => {
        setAlertRendered(false);
      }, 251);
      setAlertVisible(false);
    }, 3000);
  };

  useEffect(() => {
    renderAlert();
  }, [stateTrigger])

  const ele = (
    <div className={Alert__Container}>
      { alertRendered && (
        <div className={`${Alert} ${alertVisible ? open : ''}`}>
          <DesignLibAlert label={label} color={color}/>
        </div>
      )}
    </div>
  );

  return portalElement
    ? ReactDOM.createPortal(ele, portalElement) : null;
};

export default Alert;

CodePudding user response:

Though it's not common to "reach" into other components and invoke functions, React does allow a "backdoor" to do so.

  • Edit is-it-possible-to-expose-a-function-defined-within-a-react-function-component-to

    A more React-way to accomplish this might be to abstract the Alert state into an AlertProvider that renders the portal and handles the rendering of the alert and provides the renderAlert function via the context.

    Example:

    import { createContext, useContext, useState } from "react";
    
    interface I_Alert {
      renderAlert: (text: string) => void;
    }
    
    const AlertContext = createContext<I_Alert>({
      renderAlert: () => {}
    });
    
    const useAlert = () => useContext(AlertContext);
    
    const AlertProvider = ({ children }: { children: React.ReactElement }) => {
      const [text, setText] = useState<string>("");
      const [alertVisible, setAlertVisible] = useState<boolean>(false);
      const [alertRendered, setAlertRendered] = useState<boolean>(false);
    
      ...
    
      const renderAlert = (text: string): void => {
        setAlertRendered(false);
        setAlertVisible(false);
        setText(text);
    
        setTimeout(() => {
          setAlertVisible(true);
        }, 5);
        setAlertRendered(true);
    
        setTimeout(() => {
          setTimeout(() => {
            setAlertRendered(false);
          }, 251);
          setAlertVisible(false);
        }, 3000);
      };
    
      const ele = <div>{alertRendered && <div> ..... </div>}</div>;
    
      return (
        <AlertContext.Provider value={{ renderAlert }}>
          {children}
          // ... portal ...
        </AlertContext.Provider>
      );
    };
    

    ...

    const CopyButton = () => {
      const { renderAlert } = useAlert();
    
      const clickHandler = () => {
        renderAlert("Text copied!");
      };
    
      return (
        <>
          <button onClick={clickHandler}>Copy Your Text</button>
        </>
      );
    };
    

    ...

    function App() {
      return (
        <AlertProvider>
          ...
          <div className="App">
            ...
            <CopyButton />
            ...
          </div>
          ...
        </AlertProvider>
      );
    }
    

    Edit is-it-possible-to-expose-a-function-defined-within-a-react-function-component-to (forked)

  • Related