前言
前些日子在做项目的时候,需要封装一个Toast
组件。我想起之前用过的库,只要在入口文件中引入就可以在全局中使用,还是很方便的,借这次机会也来实现一下。说起来也算是forwardRef
、useImperativeHanle
和useContext
的实际使用。
- 第一种,使用
forwardRef
和useImperativeHanle
一个是像react-toastify
库一样使用,在入口处放置ToastContainer
,然后在代码中任意地方使用toast("Wow so easy!")
都有提示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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>Notify!</button> </div> ); } |
- 第二种,使用
useContext
在入口处放置ToastProvider
,然后在代码中任意地方使用 const { show } = useToast()
都有提示。忘记什么库了。
文中就用antd
的message
来模拟一下我们自己写的Toast
组件。
正文
我们先来了解一下forwardRef
、useImperativeHanle
和useContext
的基本使用。
forwardRef
和 useImperativeHandle
的基本使用
forwardRef
和 useImperativeHandle
,它们通常一起使用,以便在父组件中暴露子组件的特定方法或属性。
forwardRef
,它允许你将父组件的ref
转发到子组件中的某个 DOM 节点或其他 React 组件。这样,父组件就可以访问子组件的引用,并直接操作它。
useImperativeHandle
是一个自定义 Hook,它允许你自定义通过 forwardRef
暴露给父组件的 ref
值。你可以指定哪些方法
或属性
被暴露,而不是直接暴露整个 DOM 节点或组件实例。
下面是一个简单的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import React, { forwardRef, useImperativeHandle, useRef } from 'react' ; const ChildComponent = forwardRef((props, ref) => { const inputRef = useRef( null ); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); }, })); return ; }); const ParentComponent = () => { const childRef = useRef( null ); const handleClick = () => { childRef.current.focus(); }; return ( <div> <button>Focus Child Input</button> </div> ); }; export default ParentComponent; |
使用forwardRef和useImperativeHanle封装全局Toast
封装组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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 {} }>(); export const ToastContain = () => { return } export const showToast = (msg: string) => { if (ToastRef.current) { ToastRef.current.show(msg) } }; |
在入口中引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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( 准备中 |
} />
);
reportWebVitals();