/ React

PrimeReact growl with Redux

4youngpadawans.com presents PrimeReact growl with Redux featuring React | PrimeReact


Growl is UI popup notification that contains information important for a user. Some operating systems have its own Growl API and also there is ongoing attempt to standardize HTML5 web notifications API.
In this tutorial I will try to create practical example and explain how to create growl notification system for React SPA using PrimeReact Growl component and Redux bindings for React.

Primary goal

The primary goal of our efforts is:

  • to design our app on such a way that there is only one occurrence of Growl component (global growl) inside app's root component <App/>,
  • to provide mechanism that any UI or container component (regardless of where it is located in hierarchy of components) can show growl with single function call

Basics

Before going to next steps, lets suppose that you've already

In order to achieve our goal, we will need to implement following

  • to define redux action that basically represents our intention to show growl notification,
  • to create redux reducer that defines the way how our action changes app's state,
  • to give ability to any component to dispatch redux action and change redux store (app's state),
  • to create redux store and bind redux reducer(s) to it.
  • to allow app's top level component (where we will place our growl component) to subscribe and listen on state changes caused by redux action.
  • to intercept state changes, read growl information and, finally, show growl notification.

Redux action

We can start by creating redux action that should contain 2 fields

  • action type - so that our logic can differentiate actions supposing show growl action will not be the only action in our app,
  • action message - placeholder for growl details (e.g. growl title, details text,...)

actions/actions.jsx

//action type
export const SHOW_MESSAGE = 'SHOW_MESSAGE'

//action
export const showMessage = (message) => ({
  type: SHOW_MESSAGE,
  message:message
})

export function showGrowl(message) {
  return showMessage(message);
}

Redux reducer

Reducer should intercept our action (intent to show growl), take action's message property which carries growl details and change the state by pushing message into state's messages property.

reducers/message-reducer.js

import {SHOW_MESSAGE} from '../actions/actions.jsx';

const growlmessages = (state = {messages:[]}, action) => {
	switch (action.type) {
	case SHOW_MESSAGE:
        console.log('message-reducer action', action);
        let messages=[];
        messages.push(action.message);
		return {messages:messages};
    default:
        return state;
  }
}

export default growlmessages

Notice that I used ES6 syntax to define state's initial value: state = {messages:[]}

Combine all reducers

Supposing that our real app will have more then one reducer, Redux provides handy way to combine all reducers.
reducers/reducers.js

import { combineReducers } from 'redux'
import growlmessages from './message-reducer.js'
//for example, lets suppose we have another action that triggers deletion of some items
import deleteitem from './deleteitem-reducer.js'

const appReducers = combineReducers({
  growlmessages,deleteitem
})

export default appReducers

Redux store

Redux store is object that holds entire app's state. It is unique for entire application. The only allowed way to change state is by dispatching an action.
Store is created by passing all app's reducers to it.
index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { createStore } from 'redux'
import appReducers from './reducers/reducers.js'

//here we create store by passing all combined reducers
let store = createStore(appReducers);

const render = () =>
  ReactDOM.render(
    <Provider store={store}>
        <App/>
	</Provider>,
    document.getElementById('root')
  );

render();

<Provider/> is another Redux short hand that exposes store to all components inside top level <App/> and allows all components to be able to dispatch action or subscribe and listen to dispatched actions.

Dispatching redux actions

Now let's implement action creator: React component that will dispatch (trigger, create) an action with intent to show growl notification.
In following example, we will define 4 buttons and each of them will trigger different PrimeReact's growl type (success, info, warning, error)
primereact-growl.jsx

import React, { Component } from 'react';
import { connect } from 'react-redux'
import { showGrowl } from '../actions/actions.jsx'
import { showSuccessMessage } from '../utils/utils.js';
import { showInfoMessage } from '../utils/utils.js';
import { showWarningMessage } from '../utils/utils.js';
import { showErrorMessage } from '../utils/utils.js';
import { Button } from 'primereact/components/button/Button';

export class GrowlTest extends Component {
    constructor() {
        super();
    }

    onShowSuccessMessage = () => {
        showSuccessMessage(this, "System", "I am success growl");
    };

    onShowInfoMessage = () => {
        showInfoMessage(this, "System", "I am info growl");
    };

    onShowWarningMessage = () => {
        showWarningMessage(this, "System", "I am warning growl");
    };

    onShowErrorMessage = () => {
        showErrorMessage(this, "System", "I am error growl");
    };

    render() {
        return (
            <div>
                <div className="bottom-margin-small">
                    <Button label="Show success growl" onClick={this.onShowSuccessMessage} className="ui-button-success" />
                </div>
                <div className="bottom-margin-small">
                    <Button label="Show info growl" onClick={this.onShowInfoMessage} className="ui-button-info" />
                </div>
                <div className="bottom-margin-small">
                    <Button label="Show warning growl" onClick={this.onShowWarningMessage} className="ui-button-warning" />
                </div>
                <div className="bottom-margin-small">
                    <Button label="Show error growl" onClick={this.onShowErrorMessage} className="ui-button-danger" />
                </div>
            </div>
        )
    };
}

const mapDispatchToProps = {
    showGrowl
};

GrowlTest = connect(null, mapDispatchToProps)(GrowlTest)

Functions showSuccessMessage, showInfoMessage, showWarningMessage and showErrorMessage are just wrappers around core function showGrowl that dispatches an action
utils/utils.js

//core function that dispatches 'showGrowl' action
export function showGrowlMessage(sender,messageSeverity, messageSummary, messageDetail) {
    sender.props.showGrowl({life: 2000, severity: messageSeverity, summary: messageSummary, detail: messageDetail });
}

export function showSuccessMessage(sender, messageSummary, messageDetail) {
    showGrowlMessage(sender,'success',messageSummary, messageDetail)
}

export function showInfoMessage(sender, messageSummary, messageDetail) {
    showGrowlMessage(sender,'info',messageSummary, messageDetail)
}

export function showWarningMessage(sender, messageSummary, messageDetail) {
    showGrowlMessage(sender,'warn',messageSummary, messageDetail)
}

export function showErrorMessage(sender, messageSummary, messageDetail) {
    showGrowlMessage(sender,'error',messageSummary, messageDetail)
}

Redux connect() and mapDispatchToProps()

These are two Redux shortcut notations that wrap up a lot of boilerplate code.

  • connect() is binding component to redux store allowing components to change store (app's state) by dispatching actions with some data.
  • mapDispatchToProps() allows us to elegantly dispatch an action by changing React component's props.

Subscribing to redux actions

Since our app is now capable to dispatch and propagate actions which are subsequently changing app's store, we are ready to subscribe and listen to data (payload) carried by action.

import React, { Component } from 'react';
import './App.css';
import 'primereact/resources/themes/omega/theme.css';
import 'primereact/resources/primereact.min.css';
import 'font-awesome/css/font-awesome.css';
import { Growl } from 'primereact/components/growl/Growl';
import { connect } from 'react-redux'

class App extends Component {
  componentWillReceiveProps(nextProps) {
    if (nextProps.growlmessages.messages) {
      if (nextProps.growlmessages.messages.length > 0) {
        this.growl.show(nextProps.growlmessages.messages);
      }
    }
  }

  render() {
    return (
      <div className="App">
        <Growl ref={(el) => { this.growl = el; }}></Growl>
        
        <div className="App-content">
        {this.props.children}
        </div>
      </div>
    );
  }
}

const mapStateToProps = (state) => (
  { growlmessages: state.growlmessages }
)
App = connect(mapStateToProps)(App)

export default App;

Notice that mapStateToProps() is another handy Redux notation that actually allows our component to listen to state changes (triggered by dispatching certain action), and every time when state is modified, receive changes as new props.

Result

We pretty much achieved our goal

  • top level component contains PrimeReact Growl component and it is the only occurrence of growl component in scope of application,
  • our action creator component GrowlTest contains 4 buttons and each of them dispatches an action with intent to show growl using single function call, for example showErrorMessage(this, "System", "I am error growl");
  • message-reducer.js intercepts an action and updates app's state with action's data (in this case, growl title, details,...),
  • top level <App/> component, with a help of Redux mapStateToProps() which transforms state changes to nextProps, listens and intercepts nextProps in componentWillReceiveProps(nextProps) and, finally, shows growl notification on the screen.
    primereact_growl_redux
PrimeReact growl with Redux
Share this