Notifications with React and Redux

Update 14/03/2017 – better implementation

After recently completing the excellent Reddit API tutorial from the official Redux documentation, I got to implementing a very similar set up in my own application.

One thing pointed out – but not really covered in the tutorial – is that sometimes (more often than I would like to believe, no doubt), communication between the front end and the back end will break.

Now, this sucks for many reasons. But from a user’s perspective, my reasoning is they won’t be as bothered if a little notification / pop up helpfully pops up and says: “heads up, that didn’t work… try again!”

Ok, so they try again and it doesn’t work again. Bad times. Maybe I need to work on my helpful error messages. Or better yet, fix the damn system.

But, message content and flaky systems aside, I decided I wanted to keep track of my notifications via Redux / the application state.

I figured this would be a solved problem. And it is. Kind of.

I found quite a few libraries that already solve this problem, in one way or another:

  • React Toolbox – Snackbar component
  • Igor Prado’s React Notification System
  • Patrick Burtchaell’s React Notifications

And there were others.

I also found a lot of not-React-ified code:

  • Toastr
  • Growler
  • Alertify JS

There’s probably a ton more. It seems like a common problem, with a variety of solutions.

But none of them were Redux-ready, right out of the box. At least, not that I could see.

Being new to Redux, I found this a little worrying (ack, how to do this myself?!) but also an interesting challenge (ack, with what I know so far, can I even do this myself?)

It’s worth noting that the React Toolbox Snackbar component does everything I need. Only, it has a material design theme to it, which I didn’t want. I also didn’t want to have to start styling it. If material design is your thing, that one is a solution.

Then there’s Alertify JS, which is a lightweight, snazzy little bit of code that nails the alerting / notification requirement perfectly. Only there’s a problem.

Alertify JS is an immediately-invoked function function expression (IIFE), which is cool in so much as it “just works”. But it’s not so cool in that I need to hook this up using Redux and I couldn’t figure out a way to make it work how I wanted.

The situation I have is that I want to dispatch a new action every time I need to display a notification. The action would hit the reducer, which would update the state. The state would flow down into my notification component and render a nice notification.

Alertify did not play well with this. As an IIFE, where could I put the alertify.log(‘some message’); method in a way that makes sense to be powered by props? If anywhere, it made most sense to put the call in the reducer switch statement. But that’s not redux.

So that was enough to have ruled out both React Toolbox, and Alertify JS. Onwards.

It boiled down to either React Notifications, or the React Notification System. Neither are pre-configured for Redux. Both fit the component / props requirement though.

Ultimately, I went with the React Notification System, simply as the styling looked like what I wanted. And I very much dislike doing any styling if I really can avoid it.

Ok, so some code.

import React, { PropTypes } from 'react';
import SomeOtherComponent from '../containers/SomeOtherComponent';
import NotificationContainer from '../containers/Notification';

const App = (props) => {
  return (
      <div className="body">
        <SomeOtherComponent props/>
        <NotificationContainer props/>
      </div>
  );
};

export default App;

First thing, add the Notification container on the top level component.

export const ADD_NOTIFICATION = 'ADD_NOTIFICATION';

The action type / constant that matches this action.

import { ADD_NOTIFICATION } from '../constants/ActionTypes';

export function addNotification(message, level) {
  return {
    type: ADD_NOTIFICATION,
    message,
    level
  };
}

Declaring the add notification action function is really straightforward. Just like any other redux action, a simple function containing a payload of data.

import {
  ADD_NOTIFICATION
} from '../constants/ActionTypes';

export default function notification(state = {}, action) {
  switch (action.type) {
    case ADD_NOTIFICATION:
      return Object.assign({}, state, {
        message: action.message,
        level: action.level
      });

    default:
      console.debug('notification reducer :: hit default', action.type);
      return state;
  }
}

The reducer is also very straightforward. As mentioned, I want a component that receives some props (that can change by way of a redux managed state update), and then display a new notification accordingly.

Also, don’t forget to enable this reducer by adding it to the rootReducer :

import { combineReducers } from 'redux';
import {routerReducer} from 'react-router-redux';
import otherReducer from './otherReducer';
import notification from './notificationReducer';

const rootReducer = combineReducers({
  routing: routerReducer,
  otherReducer,
  notification
});

export default rootReducer;

At this stage, I am not concerned with different types of notifications, or doing anything particularly fancy at all. Just some text, and a level, which is React Notification System’s terminology for success, info, warning, etc.

Notice also, the console.debug  statement on the default switch  case? That’s because of this.

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { addNotification } from '../actions/notificationActions';
import NotificationSystem from 'react-notification-system';

class NotificationContainer extends Component {

  constructor(props) {
    super(props);
  }

  componentDidMount() {
    this.notificationSystem = this.refs.notificationSystem;
  }

  componentWillReceiveProps(newProps) {
    const { message, level } = newProps.notification;
    this.notificationSystem.addNotification({
      message,
      level
    });
  }

  render() {
    return (
      <NotificationSystem ref="notificationSystem" />
    );
  }
}

function mapStateToProps(state) {
  return {
    notification: state.notification
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators({
      addNotification
    }, dispatch)
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(NotificationContainer);

The real hard work of actually rendering a notification is given to React Notification System. I just need a way of adding a new notification whenever this component receives some new props.

Anyway, it all works really nicely so far.

I have a variety of actions that make use of fetch, and I’m doing this (simplified to show the dispatching):

return fetch('https://api.some-site.com/whatever.json', { method: 'POST' })
  .then(res => {
    dispatch(addedSomethingElse(somethingElse, res));
    dispatch(addNotification('happy days', 'success'));
  })
  .catch(err => {
    dispatch(failedAddingSomethingElse(somethingElse, err));
    dispatch(addNotification('it went horribly wrong', 'error'));
  });

I’m incredibly new to ES6, React, Redux, and most other parts of the ecosystem, so:

  • if I used any terminology incorrectly;
  • could improve my practices in any way;
  • or did this all wrong!

Then please do leave a comment and let me know 🙂

 

Learning Redux, Or 4 Days On A Typo

In the evenings as of late, I have been diving head first into a brand new tech stack (for me):

  • React
  • Redux
  • React Router

This doesn’t cover all the related technologies that enable this stack to function (webpack, babel, and a whole load more).

I figured learning React, in a general sense, would be fairly challenging, but ultimately worthwhile. I definitely still feel this way.

It took me a while to get my head around React, but once I had, things started dropping into place.

So, I added Redux to the mix.

I followed the Todo tutorial over at the Redux docs, which went well.

Then I set myself a little challenge. I read through the Async Redux tutorial but decided: rather than simply type out what I saw, this time, I would try and reproduce the functionality, but without referring to the docs. Unless very stuck.

The finished project should be able to fetch a list of posts from a given subreddit, and display them in a list. Then, a drop down box should allow changing the subreddit, and also invalidating any locally saved Reddit post titles. Fairly basic in truth.

Things started well. It was a Friday night so beer was involved, but the general functionality for fetching posts, dispatching a request and receive action, and handling those actions seemed in place.

But for some reason, everything kinda died when posts were received.

It’s well worth pointing out, at this point, how useful the Redux Logger middleware can be for getting a feel for just what the heck is happening in your application as the state changes.

However, even with Redux Logger, I still couldn’t figure out what was going wrong. The posts were definitely being requested. The response was being received, and the array containing the posts was populated.

But the next state just would not contain the array posts.

I’m somewhat ashamed to say it took essentially 4 days to spot this typo. But it did, and during that time I learned a really useful, if somewhat fundamentally basic bit of debugging advice:

For the love of God, put a console.log() on your switch default statements.

Also, repeat after me: I before E, except after C.

Sad panda.

export const REQUEST_POSTS = 'REQUEST_POSTS'
export const RECEIVE_POSTS = 'RECEIVE_POSTS'

and:

function postsBySubreddit(state = { }, action) {
  switch (action.type) {
    case INVALIDATE_SUBREDDIT:
    case RECEIVE_POSTS:
    case REQUEST_POSTS:
      return Object.assign({}, state, {
        [action.subreddit]: posts(state[action.subreddit], action)
      })
    default:
      console.log('switch on :: postsBySubreddit default', action.type);
      return state
  }
}

Yup. In my reducer I had made a basic typo (fixed above).

Frustratingly, because these were constants, WebStorm wasn’t detecting my fat fingered tomfoolery.

I wanted to share this not because it was insightful in any serious way. Rather, I want to highlight that mistakes are easily made. Learning new things is hard, and whilst something like this will lead you to drink smack your head against the wall, it is ultimately part of the learning process.

I see all too frequently how beginners get easily frustrated, and worst, believe that experienced programmers know exactly what to do for any situation, and never make mistakes. If only 🙂

Anyway, I strongly recommend the Redux tutorials, and pretty much anything else that Dan Abramov puts out there. This guy is super smart, and an amazing teacher. Thank you Dan!

How I Fixed: Warning: React.createElement: type should not be null, undefined, boolean, or number.

I’ve been going through a lot of React JS training materials and tutorials as of late, and am slowly getting the point where I try to do it myself first, without simply typing out the tutorial pages.

Tonight, I hit upon a fun issue which I couldn’t find an immediate Google / Stack Overflow answer for. Perhaps my Google-foo is weak.

The example I was working from was the brilliant Redux Reddit API tutorial from the official Redux docs.

The two errors I kept running into were:

Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of `AsyncApp`.

// and

Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of `AsyncApp`.

Both found in the browser console log, and stopped the app from rendering.

The solution here is really straightforward, but let’s see the code I was using, to see if you can spot the problem first:

import React, { Component } from 'react';
import { fetchPosts } from '../actions/actions';
import { Posts } from '../components/Posts';

export default class AsyncApp extends Component {

  constructor(props) {
    super(props);
  }

  render() {
    return (
      <Posts />
    );
  }
}

And:

import React, { Component } from 'react';

export default class Posts extends Component {
  render() {
    return (
      <ul>
        <li>hello 1</li>
        <li>hello 2</li>
        <li>hello 3</li>
      </ul>
    );
  }
}

The cause was certainly not immediately obvious to me. But here’s the issue:

export default class Posts extends Component

and then, in AsyncApp I was doing:

import { Posts } from '../components/Posts';

Well, if the class being exported is default then the import statement should instead be :

import Posts from '../components/Posts';

I found this Stack Overflow explanation very helpful in expanding my understanding of why this issue occurred.

Basic React ES6 Class Example

Sometimes it’s the super easy stuff I forget how to do.

It’s really straightforward to create a WebStorm Live Template for a React ES6 Class template.

But when you’re first starting to learn things like this, I find just repeatadly typing it out every time I want to make a new React ES6 class really helps grind it into my memory:

import React from 'react';

class YourClassNameHere extends React.component {

  render() {
    return (
      <div className="yourCssClassName">

      </div>
    );
  }

}

YourClassNameHere.propTypes = {
  pair: React.PropTypes.array.isRequired
};

export default YourClassNameHere;