在 React/Redux 应用程序中正确隔离组件,但允许在彼此之间传递回调

Posted

技术标签:

【中文标题】在 React/Redux 应用程序中正确隔离组件,但允许在彼此之间传递回调【英文标题】:Properly isolating components in React/Redux app, but permit passing callbacks between each others 【发布时间】:2018-01-06 16:24:06 【问题描述】:

我刚开始用 Redux 学习 React,所以请多多包涵。 我遵循 Redux 有点入门,到目前为止,一切都很好。但是按照“基本”和“高级”redux 文档部分,我将在我的所有组件都被隔离的位置结束。这很好。除了我的一个组件旨在显示通知、关闭和删除它们。 另一种是模式,需要 4 个参数才能工作:一个带有标题文本部分,与正文部分相同,一个布尔值用于设置是否显示,一个用于在单击确认按钮时触发确认操作。 当用户想要删除通知时,我的通知组件需要显示模式。两个组件都有一个由 redux 处理的状态。

这里有一些片段。

下面是通知缩减程序,它处理解除删除和获取通知。很简单。

// The notifications reducer
let initialState = 
  isFetching: false,
  didInvalidate: false,
  notifications: []


const notifications = (state = initialState, action) => 
  switch (action.type) 
    case 'DISMISS':
      return ...state, notifications: state.notifications.map(element => element.id === action.id ? ...element, dismissed: !element.dismissed : element)
    case 'DISMISS_ALL':
      return ...state, notifications: state.notifications.map(element =>  return ...element, dismissed: false ) 
    case 'DELETE':
      return ...state, notifications: state.notifications.filter(element => element.id !== action.id)
    case 'DELETE_ALL':
      return ...state, notifications: []
    case 'INVALIDATE':
      return ...state, didInvalidate: true
    case 'REQUEST':
      return ...state, isFetching: true, didInvalidate: false
    case 'RECEIVE':
      return ...state, isFetching: false, didInvalidate: false, notifications: action.posts, lastUpdate: action.receivedAt
    default:
      return state
  


export default notifications

然后是通知操作。我这里的重点是deleteNotificationdeleteAllNotificationsactions,它们必须绑定模态打开并等待其响应,然后触发删除,并且仅在此之后。 那些需要(我猜)像fetchNotifications 操作一样工作,调用另一个操作(模态的),而不是像fetchNotifications 那样进行数据获取。

// The notifications actions
//@flow
import axios from 'axios'

/**
 * NOTIFICATIONS ACTION CREATORS
 */
export const dismissNotification = (id: number) => 
  return  type: 'DISMISS', id 

export const dismissAllNotifications = () => 
  return  type: 'DISMISS_ALL' 

export const deleteNotification = (id: number) => 
  return  type: 'DELETE', id 

export const deleteAllNotifications = () => 
  return  type: 'DELETE_ALL' 


/**
 * DATABASE NOTIFICATIONS ACTION CREATORS
 */
export const invalidate = (uri: string) => 
  return  type: 'INVALIDATE', uri 

export const requestNotifications = (uri: string) => 
  return  type: 'REQUEST', uri 

export const receiveNotifications = (uri: string, json: string) => 
  return 
    type: 'RECEIVE',
    uri,
    posts: json,
    receivedAt: Date.now()
  

export const fetchNotifications = (uri: string) => 
  return (dispatch: any) => 
    dispatch(requestNotifications(uri))
    return axios.get(uri)
      .then(
        response => response.data,
        error => console.log('Error', error) // TODO logger les erreurs
      )
      .then(json => 
        dispatch(receiveNotifications(uri, json))
      )
  

最后,模态的减速器。 reducer 的重点是通过headerMessagebodyMessage 处理显示(可见或不可见)中的文本。 id 在这里告诉模态是否必须在一个或一大堆元素上调度删除。这对我来说很脏。例如,它应该只返回一个确认,通知操作将捕获该确认作为响应。

let initialState = 
  showModal: false,
  headerMessage: '',
  bodyMessage: '',
  id: false


const modal = (state = initialState, action) => 
  switch (action.type) 
    case 'MODAL_DISPLAY':
      return ...state,
        showModal: action.showModal,
        headerMessage: action.headerMessage,
        bodyMessage: action.bodyMessage,
        id: action.id
      
    case 'MODAL_HIDE':
      return ...state,
        showModal: action.showModal,
        headerMessage: action.headerMessage,
        bodyMessage: action.bodyMessage,
        id: action.id
      
    default:
      return state
  


export default modal

需要明确的是,模态组件和通知组件位于不同的目录中。我希望模态组件可以在任何上下文中使用。这里是关于通知的,但如果我希望它与通知之外的任何其他组件一起使用,比如说用户的个人资料,我不应该专门绑定到任何组件。

就我而言,通知组件是调用模式的组件。这就是“驱动”全球行为的原因。但我想我没有把这一切做好。这是我的通知组件索引:

//@flow

import React from 'react'
import  render  from 'react-dom'
import thunkMiddleware from 'redux-thunk'
import  createLogger  from 'redux-logger'
import  Provider  from 'react-redux'
import  createStore, applyMiddleware, compose  from 'redux'
import  fetchNotifications  from './store/actions/NotificationsActions'
import NotifiationsApp from './store/reducers/reducerCombiner'
import App from './components/App'
import ModalContainer from '../modal-component/store/containers/ModalContainer'

const loggerMiddleware: any = createLogger()
const composeEnhancers: any = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store: any = createStore(
  NotifiationsApp,
  composeEnhancers(
    applyMiddleware(
      thunkMiddleware,
      loggerMiddleware
    )
  )
)

store.dispatch(fetchNotifications('/app_dev.php/notifications/load?offset=0&limit=10'))

render(
  <Provider store=store>
    <div>
      <App/>
      <ModalContainer />
    </div>
  </Provider>,
  document.getElementById('notifications-component')
)

如您所见,我将ModalContainer 称为“智能”模态组件抽象。 最后,这是我的错; ModalContainer 与通知组件相关联,我想在onConfirm 被触发时“返回”确认事件:

import  connect  from 'react-redux'
import  deleteAllNotifications, deleteNotification  from '../../../notifications-component/store/actions/NotificationsActions'
import  hideModal  from '../actions/ModalActions'
import ModalInstance from '../../components/Modal'

const getModalProperties = modal => 
  return modal


const mapStateToProps = state => 
  return getModalProperties(state.modal)


const mapDispatchToProps = dispatch => 
  return 
    onCancel: () => 
      dispatch(hideModal(false, false, '', ''))
    ,
    onConfirm: id =>  // BELOW, THIS IS VERY BAD!!
      id ? dispatch(deleteNotification(id)) : dispatch(deleteAllNotifications())
      dispatch(hideModal(false, '', '', false))
    
  


const ModalDisplay = connect(
  mapStateToProps,
  mapDispatchToProps
)(ModalInstance)

export default ModalDisplay

我在这方面苦苦挣扎。请帮忙!

【问题讨论】:

【参考方案1】:

我不确定我是否理解正确,但您似乎正在寻找ownProps。您想将一个函数传递给您的 ModalWindow 组件,该函数应在调用确认函数后调用,对吗?这样你就有了像

这样的界面
<ModalContainer onConfirm=myHandler/>

在这种情况下,您的mapDispatchToProps 需要考虑您传递给容器的道具,例如

const mapDispatchToProps = (dispatch, ownProps) => 
  return 
    onCancel: () => 
      dispatch(hideModal(false, false, '', ''))
    ,
    onConfirm: id =>  // BELOW, THIS IS VERY BAD!!
      id ? dispatch(deleteNotification(id)) : dispatch(deleteAllNotifications())
      dispatch(hideModal(false, '', '', false));
      ownProps.onConfirm && ownProps.onConfirm(id);
    
  

【讨论】:

【参考方案2】:

总的来说,您走在正确的轨道上。方便的是,我刚刚发布了Practical Redux, Part 10: Managing Modals and Context Menus,它演示了如何使用 Redux 构建和控制模式、上下文菜单和通知。这包括如何在模式关闭时处理“返回值”。

具体回答您的问题:处理 ID 以清除一个通知与所有通知对我来说根本不“脏”或“坏” - 这种方法应该没问题。

【讨论】:

以上是关于在 React/Redux 应用程序中正确隔离组件,但允许在彼此之间传递回调的主要内容,如果未能解决你的问题,请参考以下文章

在 React 中路由其他组件时重置 redux 存储

使用通过 react、redux 和 react-redux 完成的组件以及在 react 应用程序中使用 webpack 构建时出错

将firestore onSnapShot与react redux一起使用的正确方法

React Redux 使用带有连接组件的 HOC

如何在 react/redux 应用程序中开玩笑地访问组件的子组件

React: 研究React Redux的使用