Dispatch a Redux action with the subsequent action

2019-04-04 19:12发布

问题:

I'm using React with Redux and Material UI to build a webapp. The webapp is made of several pages and components. I know that a snackbar or dialog should be directly connected to what the user is doing. However, I'd like to make the snackbar and dialog independent on the pages and components. A use case therefore is displaying a message like background synchronization of your data failed and an action retry now. My idea was to render the snackbar on a page called RootFrame, which is used to wrap all other pages and to dispatch the text of the snackbar as payload of an action.

My Redux action to show a snackbar:

export function showSnackbar(message: string) {
  return {
    type: SHOW_SNACKBAR,
    payload: message
  };
}

Of course it might also be good to specify the message in the action instead of taking the message as argument but that's not my problem right now. The problem is: How can I use this system and display a snackbar with an action? Can I change my action to

export function showSnackbar(message, action) {
  return {
    type: SHOW_SNACKBAR,
    payload: {
      message, 
      action
    }
  };
}

and render my snackbar in the RootFrame like

<Snackbar
  message={this.props.message}
  ref='snackbar'
  onDismiss={() => this.props.dispatch(dismissSnackbar())}
  action='retry now'
  onActionTouchTap={() => this.props.dispatch(this.props.action)}
/>;

When the snackbar is dismissed, an action changes a variable in the state: snackbar.visible = false. This is used to activate the snackbar (it is rendered when snackbar.visible === true). When the user clicks on retry now, the action to start the sync (which is passed to the component as props) should be dispatched. The problem is very similar when using dialogs. So not only the text to display but also the next possible actions have to be passed to the component.

Do you think using Redux like this is ok or is there a better solution?

回答1:

Actually, right now usign redux was changed a little bit. We use createAction from redux-act, properly we use createReducer further. In a component we use connect decorator or class from react-redux. Connector provides redux state, dispatched actions, parent props. So for our snackbar we have:

  1. actions:

    export const showMessageTop = createAction();
    export const closeMessageTop = createAction();
    
  2. Reducer:

    import {createReducer} from 'redux-act';
    import * as ac from '../actionCreators/messageTop';
    export default createReducer(
      {
        [ac.showMessageTop]: (state, messageText) => ({messageText}),
        [ac.closeMessageTop]: () => ({messageText: ''}),
      },
      {
        messageText: window.location.search === '?login=1'
                   ? 'Welcome'
                   : '',
      }
    )
    
  3. And a component(use decorator instead of class):

    import {closeMessageTop} from '../../actionCreators/messageTop'; 
    import MessageTop from './MessageTop';
    @connect(state => ({
      // gettext: gettext(state.locale.messages),
      messageText: state.messageTop.messageText,
    }))
    export default class MessageTopContainer extends React.Component {
    ...
    <button onClick={...bindActionCreators({onClose: closeMessageTop}, dispatch)}/>
    

So in current props we have this.props.messageText. And we can show this bar if we have message, or we can invoke closeAction which sets up messageText in empty string.