Some days ago when I was working on a project, I needed to encapsulate a Toast component. I remembered that the library I used before can be used globally as long as it is introduced in the entry file, which is still very convenient, so I took this opportunity to implement it as well. This is also a practical use of forwardRef , useImperativeHanle and useContext .


  • The first, using forwardRef and useImperativeHanle


One is to use it like the react-toastify library, placing ToastContainer at the entry point and then using toast("Wow so easy!") anywhere in the code with a prompt

  import React from 'react';

  import { ToastContainer, toast } from 'react-toastify';
  import 'react-toastify/dist/ReactToastify.css';
  
  function App(){
    const notify = () => toast("Wow so easy!");

    return (
      <div>
        <button onClick={notify}>Notify!</button>
        <ToastContainer />
      </div>
    );
  }
  •  The second, using useContext


Place ToastProvider at the entry point and then use const { show } = useToast() anywhere in the code to get a hint. I forget what library.


In the article, we’ll use message from antd to simulate the Toast component that we wrote ourselves.


Let’s go over the basic use of forwardRef , useImperativeHanle and useContext .


forwardRef Basic use of useImperativeHandle


forwardRef and useImperativeHandle , which are often used together to expose specific methods or properties of a child component in the parent component.


forwardRef , which allows you to forward ref from the parent component to a DOM node or other React component in the child component. This way, the parent component can access the child component’s reference and manipulate it directly.


useImperativeHandle is a custom Hook that allows you to customize the ref value exposed to the parent component via forwardRef . You can specify which  are exposed instead of exposing the entire DOM node or component instance directly.

 Here is a simple example

import React, { forwardRef, useImperativeHandle, useRef } from 'react';

const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
  }));

  return <input ref={inputRef} {...props} />;
});

const ParentComponent = () => {
  const childRef = useRef(null);

  const handleClick = () => {
    childRef.current.focus();
  };

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={handleClick}>Focus Child Input</button>
    </div>
  );
};

export default ParentComponent;


Wrapping the global Toast with forwardRef and useImperativeHanle

 assembly

import React, { createRef, forwardRef, useImperativeHandle } from 'react';
import { Button, message } from 'antd';

const Toast = forwardRef((props, ref) => {
  const [messageApi, contextHolder] = message.useMessage();

  useImperativeHandle(ref, () => ({
    show: (msg: string) => {
      messageApi.info(msg);
    }
  }));

  return <>
    {contextHolder}
  </>
})

const ToastRef = createRef<{ show: (msg: string) => {} }>();

export const ToastContain = () => {
  return <Toast ref={ToastRef} />
}

export const showToast = (msg: string) => {
  if (ToastRef.current) {
    ToastRef.current.show(msg)
  }
};

 Introducing in the portal

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import router from '@/router/index'
import reportWebVitals from './reportWebVitals';
import { RouterProvider } from 'react-router-dom';
import ErrorBoundary from './ErrorBoundary';
import { ToastContain } from './components/Toast';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
      <ToastContain />
      <RouterProvider router={router} fallbackElement={<div>ing...</div>} />
  </React.StrictMode>
);
reportWebVitals();


Then you can use the showToast method globally

import React from 'react';
import { showToast } from '../../../components/Toast';

export default function Index() {
  return <>
    <div
      onClick={() => {
        showToast('sadasds')
      }}
    >
       pop-up window
    </div>
  </>
}

  useContext basic use


useContext Used to access Context defined at a level in the component tree. Context Provides a way to share values between components without having to explicitly pass props through each level of the component tree.

  1.  Creating a Context


First, you need to create a Context object. This can be done by calling React.createContext . You can also provide a parameter for a default value that will be used if Provider of Context is not found in the component tree.

import React from 'react';

const MyContext = React.createContext('defaultValue');

  1.  Providing Context


You need to provide this Context somewhere in the component tree. This is usually done at the top level of the component, by using the MyContext.Provider component and passing a value prop

import React from 'react';
import MyComponent from './MyComponent';
import { MyContext } from './MyContext';

function App() {
  return (
    <MyContext.Provider value="Hello from Context">
      <MyComponent />
    </MyContext.Provider>
  );
}

export default App;

  1.  utilization useContext


In components that need access to Context , you can use the useContext Hook to get the current value of Context

import React, { useContext } from 'react';
import { MyContext } from './MyContext';

function MyComponent() {
  const contextValue = useContext(MyContext);

  return <p>Context value: {contextValue}</p>;
}

export default MyComponent;

 Use useContext to encapsulate the global Toast

 assembly

import React, { createContext, useCallback, useContext, useState } from 'react';
import { Button, message } from 'antd';

const ToastContext = createContext<any>(null);

export const ToastProvider = ({ children }: any) => {
  const [messageApi, contextHolder] = message.useMessage();

  const show = useCallback((msg: string) => {
    messageApi.info(msg);
  }, [messageApi]);

  return (
    <ToastContext.Provider value={{ show }}>
      {children}
      {contextHolder}
    </ToastContext.Provider>
  );
};

export const useToast = () => {
  const context = useContext(ToastContext);
  return context;
};

 Use at the entrance

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import router from '@/router/index'
import reportWebVitals from './reportWebVitals';
import { RouterProvider } from 'react-router-dom';
import ErrorBoundary from './ErrorBoundary';
import { ToastProvider } from './components/ToastOne';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <ToastProvider>
      <RouterProvider router={router} fallbackElement={<div>ing...</div>} />
    </ToastProvider>
  </React.StrictMode>
);


It can then be used globally via useToast

import React from 'react';
import { useToast } from '../../../components/ToastOne';

export default function Index() {
  const { show } = useToast()

  return <>
    <div
      onClick={() => {
        show('guiyu')
      }}
    >
      Click
    </div>
  </>
}

By hbb

Leave a Reply

Your email address will not be published. Required fields are marked *