We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
实习第一周遇到一个 task 是重构一个 Dialog 组件, 看了一下项目代码发现有点东西, 原始代码我抽象了一下大致如下:
Dialog
const NavBar = () => { const handleOpen = () => { const Dialog = ( <Dialog> ... </Dialog> ) dispatch(openDialog(Dialog)) } return ( <Button onClick={handleOpen}>Open Dialog</Button> ) } const App = () => { const { component } = useSelector(state => state.dialog) return ( <div> {component && ...component} <div> ) }
// actions const openDialog = component => { return { type: 'OPEN_DIALOG', payload: { component } } } const closeDialog = () => { return { type: 'CLOSE_DIALOG', } } // reducer const dialog = (state={ component: null }, action) => { switch(action.type) { case 'OPEN_DIALOG': return { component: action.payload.component } case 'CLOSE_DIALOG': return { component: null } default: return state } }
先提一下, 公司技术栈为 React + Redux + Material UI. 简单讲一下原始代码的思路:
handleOpen
App
Reducer
我第一次遇到原来 Redux 还能这么玩... 毕竟正常 Reducer 里面应该存放可序列化的状态. 我搜了下, 发现还真有人提过这么一个类似问题: Storing React component in a Redux reducer?
很显然这么做肯定不好, 于是就让我重构了. Material UI 本身就有封装 Dialog 组件. 照着官方文档先改了一下:
const TopicDialog = props => { const { open, onClose } = props return ( <Dialog open={open} onClose={onClose}> <DialogTitle>Title</DialogTitle> <DialogContent> Content </DialogContent> <DialogActions> DialogActions </DialogActions> </Dialog> ) } const NavBar = props => { const [open, setOpen] = useState(false) const handleOpen = () => { setOpen(true) } const handleClose = () => { setOpen(false) } return ( <Button onClick={handleOpen}>Open Dialog<Button> <TopicDialog open={open} onClose={handleClose} /> ) }
思路其实很简单:
open
NavBar
TopicDialog
props
本来想着这样重构就结束了, 但是测试时候发现样式不对. 具体问题为: 由于 TopicDialog 组件放置在 NavBar 组件下, 其主题(Theme) 会直接沿用上级组件, 比如这里的 NavBar 主题是暗色主题, 那么 TopciDialog 颜色什么的都是暗色, 但我想要的主题可能是亮色的
Theme
TopciDialog
未重构前的代码没出现这样的问题, 其实可以看到, {...componet} 渲染 Dialog 组件的时候, 该 Dialog 组件是放在级别比较高的 App 里面的, 不受 Navbar 控制
{...componet}
Navbar
于是问了我的 mentor, 提供了两个思路:
Redux
第一个方法很简单, 代码基本就是这样:
const TopicDialog = props => { const { open, onClose } = props return ( <ThemeProvider theme={theme}> <Dialog open={open} onClose={onClose}> ... </Dialog> </ThemeProvider> ) }
直接用 ThemeProvider 包裹一下, 我本身不熟悉 Material UI, 不过最后还是从项目里找到了亮色主题的 theme, 导入了进来
ThemeProvider
theme
第二种方法 mentor 没有讲具体的细节, 我按照自己的思路试了一下, 先看一下抽象组件 CustomDialog, 大致如下:
CustomDialog
const CustomDialog = props => { const { dialogType } = useSelector(state => { const openedDialog = Object.entries(state.dialog) .filter(([dialogName, dialogState]) => dialogState.open === true)[0] return { dialogType: dialogType[1]['dialogType'] } }) switch(dialogType) { case 'topicDialog': return <TopicDialog /> case 'userDialog': return <UserDialog /> default: return null } }
思路:
dialogType
action 部分
action
// action const openDialog = dialogType => { return { type: 'OPEN', payload: { dialogType } } } const closeDialog = dialogType => { return { type: 'CLOSE', payload: { dialogType } } } // high order action creator const withSuffixAction = (action, suffix) => { return dialogType => { const state = action(dialogType) return { ...state, type: `${state.type}_${suffix}` } } } export const openTopicDialog = withSuffixAction(openDialog, 'TOPIC_DIALOG') export const closeTopicDialog = withSuffixAction(closeDialog, 'TOPIC_DIALOG')
reducer 部分
reducer
// reducer const topicDialog = (state, action) => { return state } const withSuffixReducer = (reducer, suffix) => { return (state={ open: false }, action) => { switch(action.type) { case `OPEN_${suffix}`: return { ...state, open: true dialogType: action.payload.dialogType } case `CLOSE_${suffix}`: return { ...state, open: false, dialogType: action.payload.dialogType } default: return reducer(state, action) } } } export const rootReducer = combineReducer({ //... 其他 reducer dialog: combineReducer({ topic: withSuffixReducer(topicDialog, 'TOPIC_DIALOG') }) })
这里逻辑和代码有些复杂, 当然也可能是我写复杂了, 具体来说有以下几个点:
topicDialog
userDialog
dialog
suffix
combineReducer
const state = { // 其他 state dialog: { topic: { dialogType: 'topicDialog', open: true, }, user: { dialogType: 'userDialog', open: false, } } }
总结来讲, 通过 dialogType 来判断是哪种 dialog 类型, open 来控制每种类型的 Dialog 的显示隐藏
最后是每一个特定的 Dialog:
const TopicDialog = props => { const { open } = useSelector(state => state.dialog.topic.open) const dispatch = useDispatch() const handleClose = () => { dispatch(closeTopicDialog('topicDialog')) } return ( <Dialog open={open} onClose={handleClose}> ... </Dialog> ) } const NavBar = props => { const dispatch = useDispatch() const handleTopicDialogOpen = () => { dispatch(openTopicDialog('topicDialog')) } return ( <Button onClick={handleTopicDialogOpen}>Open Dialog</Button> ) } const App = props => { return ( ... <CustomDialog /> ) }
close
redux
最后还是老老实实选了官方那种(覆盖 theme 的), 因为我不想写这么多代码, 以及我觉得用 Redux 来保存 Dialog 的 state 有点大材小用了...
当然我这种封装可能也不对...
完
The text was updated successfully, but these errors were encountered:
长知识了哈哈, react component 竟然还可以放到redux 里面哈哈
Sorry, something went wrong.
No branches or pull requests
引子
实习第一周遇到一个 task 是重构一个
Dialog
组件, 看了一下项目代码发现有点东西, 原始代码我抽象了一下大致如下:先提一下, 公司技术栈为 React + Redux + Material UI. 简单讲一下原始代码的思路:
handleOpen
方法里的App
(实际项目里面可能是某个级别比较高的组件) 里面, 判断Reducer
存放的 Dialog 组件是否存在, 存在直接渲染我第一次遇到原来 Redux 还能这么玩... 毕竟正常
Reducer
里面应该存放可序列化的状态. 我搜了下, 发现还真有人提过这么一个类似问题: Storing React component in a Redux reducer?很显然这么做肯定不好, 于是就让我重构了. Material UI 本身就有封装
Dialog
组件. 照着官方文档先改了一下:重构 1
思路其实很简单:
open
来控制Dialog
组件的开关Dialog
组件不在作为一个抽象概念, 而是直接放在相关组件下边, 这里是放在NavBar
里, 可以看到这个TopicDialog
组件是比较定制化的props
传递给子组件本来想着这样重构就结束了, 但是测试时候发现样式不对. 具体问题为: 由于
TopicDialog
组件放置在NavBar
组件下, 其主题(Theme
) 会直接沿用上级组件, 比如这里的NavBar
主题是暗色主题, 那么TopciDialog
颜色什么的都是暗色, 但我想要的主题可能是亮色的未重构前的代码没出现这样的问题, 其实可以看到,
{...componet}
渲染 Dialog 组件的时候, 该 Dialog 组件是放在级别比较高的App
里面的, 不受Navbar
控制于是问了我的 mentor, 提供了两个思路:
TopicDialog
用自己的亮色的主题, 覆盖掉父级组件的主题Redux
第一个方法很简单, 代码基本就是这样:
直接用
ThemeProvider
包裹一下, 我本身不熟悉 Material UI, 不过最后还是从项目里找到了亮色主题的theme
, 导入了进来重构 2
第二种方法 mentor 没有讲具体的细节, 我按照自己的思路试了一下, 先看一下抽象组件
CustomDialog
, 大致如下:CustomDialog 部分
思路:
CustomDialog
是一个抽象组件, 也是按条件渲染.Redux
连接, 根据open
属性拿到目前需要显示的dialogType
, 渲染对应的Dialog
组件redux 部分:
action
部分reducer
部分这里逻辑和代码有些复杂, 当然也可能是我写复杂了, 具体来说有以下几个点:
Dialog
组件其实都有一些相同点, 比如都存在open
属性来控制显示. 不同的地方在于可能我叫topicDialog
, 你叫userDialog
, 然后每个dialog
还可能存在一些自己的状态, 所以我选择分别用在action
和reducer
基础上封装一层, 提供一个suffix
来区分不同的dialog
combineReducer
, 所以最后的状态可能长这样:总结来讲, 通过
dialogType
来判断是哪种 dialog 类型,open
来控制每种类型的Dialog
的显示隐藏最后是每一个特定的
Dialog
:TopicDialog 部分
思路:
CustomDialog
作为一个比较基础的公共组件, 根据 reducer 里面的open
和dialogType
属性选择性渲染. 这样保证了各种Dialog
组件均在App
的 context 下, 因此 Theme 也就跟随 App 了open
和close
方法全部使用redux
里的action
, 保证状态的一致总结
最后还是老老实实选了官方那种(覆盖 theme 的), 因为我不想写这么多代码, 以及我觉得用 Redux 来保存 Dialog 的 state 有点大材小用了...
当然我这种封装可能也不对...
完
参考
The text was updated successfully, but these errors were encountered: