npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mui-modal-context

v3.0.0

Published

Imperative MUI Dialog via a React hook, while keeping the dialog inside the caller's context tree.

Readme

mui-modal-context

通过 React Hook 命令式打开 MUI Dialog,同时把对话框挂在调用方的 React 组件树里 —— 保留 Theme / I18n / Store 等上下文。

CI npm version License TypeScript React

English | 简体中文

✨ 特性

  • 🎯 命令式 API:useModal()[modal, holder],像调函数一样开关 Dialog
  • 🌳 保留上下文:Dialog 渲染在调用组件的子树里,自动继承 ThemeProvider、i18n、Redux 等所有上下文
  • React 18 / 19 全兼容:自动 JSX 运行时,无需引入 React
  • 🧩 MUI 5+ / 6+ / 7+ / 9+ 都能用:peer range 设为 >=5.11,只依赖稳定的 Dialog API
  • 🔒 严格 TypeScript:全程 strict: true,any、零 as —— ESLint 强制
  • 📦 现代化打包:Vite 8 库模式,产出 ESM + CJS + 类型声明,sideEffects: false,tree-shake 友好

📦 安装

pnpm add mui-modal-context
# 或
npm install mui-modal-context
# 或
yarn add mui-modal-context

需要本地已安装以下 peer 依赖:

pnpm add react react-dom @mui/material @emotion/react @emotion/styled

| peer | 支持版本 | | --------------- | ---------- | | react | >=18.0.0 | | react-dom | >=18.0.0 | | @mui/material | >=5.11.0 | | @emotion/react | >=11.10.0| | @emotion/styled | >=11.10.0|

🚀 快速上手

import { useModal } from 'mui-modal-context'
import { DialogTitle, DialogContent, DialogActions, Button } from '@mui/material'

export default function App() {
  const [modal, contextHolder] = useModal()

  const openDialog = () => {
    const ref = modal({
      maxWidth: 'sm',
      fullWidth: true,
      children: (
        <>
          <DialogTitle>Hello</DialogTitle>
          <DialogContent>这个 Dialog 拿得到外层的 Theme / Store / i18n。</DialogContent>
          <DialogActions>
            <Button onClick={() => ref.destroy()}>关闭</Button>
          </DialogActions>
        </>
      )
    })
  }

  return (
    <>
      <Button onClick={openDialog}>打开</Button>
      {contextHolder}
    </>
  )
}

重要:contextHolder 必须被渲染在你的 JSX 里(任意位置都行),否则 Dialog 不会显示 —— 这正是 hook 能继承外层上下文的关键。

🔧 更多用法

更新已打开的 Dialog

const ref = modal({
  children: <DialogContent>Loading...</DialogContent>
})

// 异步拿到数据后更新内容
fetchData().then((data) => {
  ref.update({
    children: (
      <>
        <DialogTitle>{data.title}</DialogTitle>
        <DialogContent>{data.body}</DialogContent>
      </>
    )
  })
})

继承外层 Context (这是核心卖点)

<ThemeProvider theme={darkTheme}>
  <I18nProvider locale="zh">
    <Provider store={store}>
      <App />
    </Provider>
  </I18nProvider>
</ThemeProvider>

App 里通过 useModal() 打开的 Dialog 会自动darkTheme、读到 zh 语言包、能 useSelector() 拿 store —— 因为它就挂在 App 的子树里,而不是被 Portal 到根节点之外的另一棵树。

Next.js App Router

源码顶部已经加了 'use client',在 Server Component 里直接 import 即可,无需自己包一层 client wrapper。

📖 API

useModal()

function useModal(): [modal: modalContentType, holder: ReactElement]

返回元组:

  • modal(config) — 打开一个 Dialog,返回 { destroy, update }
  • holder — 必须渲染到 JSX 中的承载节点

modal(config)

type ModalConfig = Omit<DialogProps, 'open'> // 接受所有 MUI DialogProps,除了 open(由库内部控制)

interface ModalHandle {
  destroy: () => void                 // 关闭并销毁(走完 transitionDuration.exit 动画)
  update: (cfg: ModalConfig) => void  // 浅合并 config(改 title / content / props 等)
}

可以在 modal() 返回之后立刻调用 destroy()update() —— 内部有 action queue 兜底,在子组件挂载完成后再执行。

如果你在 config 里自定义了 onClose,会覆盖内部的关闭逻辑(此时关闭由你接管,通常需要自己保存 ref 并调用 ref.destroy())。

🏗️ 架构 (一句话)

useModal 维护一个 ElementsHolder,每次调 modal(config) 就把一个新的 HookModal(包装 @mui/materialDialog)塞进 holder 的元素列表;HookModal 通过 useImperativeHandle 暴露 destroy / update,关闭时按 transitionDuration 等动画结束再从列表中移除。

详见 src/index.tsxsrc/modal.hook.tsx

🛠️ 本地开发

pnpm install
pnpm run typecheck   # tsc --noEmit
pnpm run lint        # eslint . (禁 any/as)
pnpm run build       # vite build → dist/

🧱 技术栈

  • TypeScript 5.x(strict)
  • Vite 8 + @vitejs/plugin-react + vite-plugin-dts
  • ESLint 9 flat config + typescript-eslint 8
  • 产物: ESM dist/index.mjs + CJS dist/index.cjs + 类型 dist/index.d.ts

🤝 贡献

欢迎 Issue / PR。开始前请阅读 CONTRIBUTING.md

📄 License

MIT © nvrenshiren

🙏 致谢