React项目中hook实现展示对话框功能
React中使用对话框并不容易,主要因为:
- 对话框需要在父组件中声明,才能在子组件中控制其是否显示
- 给对话框传递参数只能由props传入,这意味着所有状态管理都必须在更高阶的组件中。而实际上这个对话框的参数只在子组件中才会维护。这时就需要我们使用自定义事件将参数传回
这些问题的本质就是:如何用一个统一的方式去管理对话框,从而让对话框相关的业务逻辑更加模块化,以及和其他业务逻辑进行解耦。
下面的方式只是经验总结,并不是唯一或者最佳实现:
思路:使用全局状态管理所有对话框
对话框本质上是独立于其他界面的一个窗口,用于完成一个独立的功能。
所以,定义一个对话框,定位等价于定义一个具有唯一URL路径的页面。只是前者由弹出层实现,后者是页面的切换。
对话框UI弹出过程和页面URL的切换非常类似,那么我们就可以给每一个对话框定义一个全局唯一的ID,然后通过这个ID去显示或者隐藏一个对话框,并且给它传递参数。
尝试设计一个API去做对话框的全局管理
假设我们实现的对话框为NiceModal,那么我们的目标是如下去使用:
const UserInfoModal = NiceModal.create( 'user-info-modal', RealUserInfoModal ) // 创建一个useNiceModal 这样的hook去获取某个id的对话框的操作对象 const modal = useNiceModal('user-info-modal') // 通过 modal.show 显示一个对话框,并能够给它传递参数 modal.show(args) modal.hide()
可以看到,如果有这样的API,那么无论在哪个层级的组件,只要知道某个Modal的ID,那么就都可以统一使用这些对话框,而不再需要考虑该在哪个层级的组件去定义了。
实现:创建NiceModal组件和相关API
创建一个处理所有对话框的action creator 和 reducer
function showModal(modalId, args) { return { type: "nice-modal/show", payload: { modalId, args } } } function hideModal(modalId, force) { return { type: "nice-modal/hide", payload: { modalId, force } } }
const modalReducer = (state = { hiding: {} }, action) { switch (action.type) { case "nice-modal/show": const {modalId, args} = action.payload return { ...state, // 如果存在 modalId 对应的状态(即args),就显示这个对话框 // 只要有参数就认为对话框应该显示,如果没有传递args,在reducer中使用默认值true [modalId]: args || true, // 定义一个hiding 状态, 用于处理对话框关闭动画 hiding: { ...state.hiding, [modalId]: false, } } case "nice-modal/hide": const { modalId, force: boolean } = action.payload // 只有force时才真正移除对话框,否则就是隐藏中hiding return action.payload.force ? { ...state, [modalId]: false, hiding: { [modalId]: false } } : { ...state, hiding: { [modalId]: true } } default: return state } }
这段代码的主要思路就是通过Redux的store去存储每个对话框状态和参数。在这里设计了两个action,分别显示和隐藏对话框。
特别注意的是,这里加入了hiding这样的一个状态,用来处理对话框关闭过程动画。
根据使用顺序,首先实现 createNiceModal,
使用容器模式,在对话框不可见时直接返回null,从而不渲染任何内容,
确保即使页面上定义了100个对话框,也不会影响性能。
createNiceModal = (modalId, Comp) => { return (props) => { const { visible, args } = useNiceModal(modalId) if (!visible) return null return <Comp {...args} {...props} /> } } // 使用 const MyModal = createNiceModal('my-modal', () => { return ( <NiceModal id="my-modal" title="Nice modal"> Hello NiceModal </NiceModal> ) })
实现useNiceModal,根据id,封装一些逻辑。
让Redux的action使用起来更方便,在其内部封装对store的操作,从而实现对话框状态管理的逻辑重用。
const modalCallbacks = {} const useNiceModal = (modalId) => { const dispatch = useDispatch() // 封装Redux action 用于显示对话框 const show = useCallback( (args) => { dispatch(showModal(modalId, args)) }, [dispatch, modalId] ) // 封装Redux action 用于隐藏对话框 (force: boolean) const hide = useCallback( (force) => { dispatch(hideModal(modalId, force)) }, [dispatch, modalId] ) const args = useSelector((s) => s[modalId]) const hiding = useSelector((s) => s.hiding[modalId]) // 只要有参数就认为对话框应该显示,如果没有传递args,在reducer中使用默认值true return { args, hiding, visible: !!args, show, hide } }
这样,我们就实现了一个NiceModal这样的全局对话管理框架。
这样使用:
import { Button } from 'antd' import NiceModal, { createNiceModal, useNiceModal } from "./NiceModal" const MyModal = createNiceModal("my-modal", () => { return ( <NiceModal id="my-modal" title="Nice Modal"> Hello World </NiceModal> ) }) function MyModalExample() { const modal = useNiceModal("my-modal") return ( <> <Button type="primary" onClick={() => modal.show()}> Show my modal </Button> <MyModal /> </> ) }
处理对话框的返回值
如果说对话框和页面这两种UI模式基本上是一致的,都是独立窗口完成独立逻辑。但是在用户交互上,有一定的差别:
- 对话框可能需要返回值给调用者
- 而页面切换一般不会关心页面执行的结果是什么
基于上面的NiceModal实现逻辑,现在考虑如何让调用者获得返回值。
我们可以把用户在对话框中的操作看成一个异步操作逻辑,那么用户在完成对话框中内容的操作后,就认为异步操作逻辑完成了。因此我们可以利用Promise来完成这样的逻辑。
那么,我们要实现的API如下:
const modal = useNiceModal('my-modal') // 实现一个 promise API 来处理返回值 modal.show(args).then(res => {})
事实上,要实现这样一个机制并不困难,就是在 useNiceModal 这个 Hook 的实现中提供一个 modal.resolve 这样的方法,能够去 resolve modal.show 返回的 Promise。
代码的核心思路就是将show 和 resolve 两个函数通过 Promise 联系起来。因此两个函数调用位置不一样,所以我们使用一个局部的临时变量,来存放resolve回调函数。
// 使用一个 object 缓存 promise 的 resolve 回调函数 const modalCallbacks = {}; export const useNiceModal = (modalId) => { const dispatch = useDispatch(); const show = useCallback( (args) => { return new Promise((resolve) => { // 显示对话框时,返回 promise 并且将 resolve 方法临时存起来 modalCallbacks[modalId] = resolve; dispatch(showModal(modalId, args)); }); }, [dispatch, modalId], ); const resolve = useCallback( (args) => { if (modalCallbacks[modalId]) { // 如果存在 resolve 回调函数,那么就调用 modalCallbacks[modalId](args); // 确保只能 resolve 一次 delete modalCallbacks[modalId]; } }, [modalId], ); // 其它逻辑... // 将 resolve 也作为返回值的一部分 return { show, hide, resolve, visible, hiding }; };
总结
到此这篇关于React项目中hook实现展示对话框功能的文章就介绍到这了,更多相关React hook展示对话框内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
原文出处:https://blog.csdn.net/qq_34895059/article/details/118766726
相关文章
- 复制代码 代码如下: <td> <a href="/member/life/edit_ppt/<?php echo $v->id;?>" class="btn">编辑</a> <a href="javascript:;" onclick="if(confirm('您确定删除这条记录?')){location.href='/member/life/d...2014-06-07
关于React Native报Cannot initialize a parameter of type'NSArray<id<RCTBridgeModule>>错误(解决方案)
这篇文章主要介绍了关于React Native报Cannot initialize a parameter of type'NSArray<id<RCTBridgeModule>>错误,本文给大家分享解决方案,需要的朋友可以参考下...2021-05-12React引入antd-mobile+postcss搭建移动端
本文给大家分享React引入antd-mobile+postcss搭建移动端的详细流程,文末给大家分享我的一些经验记录使用antd-mobile时发现我之前配置的postcss失效了,防止大家踩坑,特此把解决方案分享到脚本之家平台,需要的朋友参考下吧...2021-06-21- 这篇文章主要介绍了React使用高德地图的实现示例(react-amap),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-18
使用 React 和 Threejs 创建一个VR全景项目的过程详解
这篇文章主要介绍了使用 React 和 Threejs 创建一个VR全景项目的过程详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-06基于BootStrap Metronic开发框架经验小结【六】对话框及提示框的处理和优化
这篇文章主要介绍了基于BootStrap Metronic开发框架经验小结【六】对话框及提示框的处理和优化的相关知识,主要对比说明在Bootstrap开发中用到的这些技术要点,对此文感兴趣的朋友一起学习吧...2016-05-14- 思路其实没有那么复杂,把地图想成一个盒子容器,地图中心点想成盒子中心点;扎点在【地图中心点】不会动,当移动地图时,去获取【地图中心点】经纬度,设置某个位置的时候,将经纬度设置为【地图中心点】即可...2021-06-20
- 这篇文章主要为大家详细介绍了React列表栏及购物车组件使用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-06-28
- 这篇文章主要介绍了react使用antd表单赋值,用于修改弹框的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-10-29
- 这篇文章主要介绍了React Native 启动流程简析,文以 react-native-cli 创建的示例工程(安卓部分)为例,给大家分析 React Native 的启动流程,需要的朋友可以参考下...2021-08-18
- 这篇文章主要介绍了React中使用setInterval函数的实例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-06
- 本文主要介绍了react为什么不推荐使用index作为key,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-07-22
- 这篇文章主要介绍了react+ts实现简单jira项目,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-07-30
- 这篇文章主要介绍了react hooks入门详细教程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-06
- 这篇文章主要介绍了一百多行代码实现react拖拽hooks,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-21
关于antd tree和父子组件之间的传值问题(react 总结)
这篇文章主要介绍了关于antd tree 和父子组件之间的传值问题,是小编给大家总结的一些react知识点,本文通过一个项目需求实例代码详解给大家介绍的非常详细,需要的朋友可以参考下...2021-06-02- 这篇文章主要介绍了react自动化构建路由的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-23
- 高阶组件就是接受一个组件作为参数并返回一个新组件(功能增强的组件)的函数。这里需要注意高阶组件是一个函数,并不是组件,这一点一定要注意,本文给大家分享React 高阶组件HOC使用小结,一起看看吧...2021-06-13
- useState 通过在函数组件里调用它来给组件添加一些内部 state,React 会在重复渲染时保留这个 state,接下来通过一个示例来看看怎么使用 useState吧...2021-06-04
- 这篇文章主要介绍了React中组件之间通信的方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-07-27