DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • How to Build Slack App for Audit Requests
  • Unleashing the Power of GPT: A Comprehensive Guide To Implementing OpenAI’s GPT in ReactJS
  • Streamlining Your Workflow With the Jenkins HTTP Request Plugin: A Guide to Replacing CURL in Scripts
  • Auto-Scaling a Spring Boot Native App With Nomad

Trending

  • Fixing Common Oracle Database Problems
  • Internal Developer Portals: Modern DevOps's Missing Piece
  • The Role of Functional Programming in Modern Software Development
  • Create Your Own AI-Powered Virtual Tutor: An Easy Tutorial
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. Real-World ReactJS and Redux (Part 2)

Real-World ReactJS and Redux (Part 2)

This is the second in a series of blog posts about real-world ReactJS usage and what we've learned scaling our app at Threat Stack.

By 
Cristiano Oliveira user avatar
Cristiano Oliveira
·
Updated Aug. 13, 22 · Analysis
Likes (1)
Comment
Save
Tweet
Share
14.7K Views

Join the DZone community and get the full member experience.

Join For Free

This is the second in a series of blog posts about real-world ReactJS usage and what we've learned about scaling our app at Threat Stack.

In this post, we'll be displaying actions based on an api middleware shown in Part 1.

Getting Everyone on the Same Page

It is critically important to ensure that all team members are on the same page at all times when it comes to the kind of products and applications that are being created. ReactJS is something that will require somewhat of a learning curve, but it can be surmounted once people have had the time that they need to get it figured out. 

It is fair to say that it is not always the easiest thing in the world to learn a brand new system, but many people have picked up on how to make ReactJS work for them, and we want to recognize those individuals and make it abundantly clear that it is something that can be accomplished. 

Everyone should want to learn what they can about ReactJS anyway as it is likely to become a much larger part of their lives very soon. 

In this post, we'll be displaying actions based on an api middleware shown in Part 1.

Still making sure to answer these questions:

  • What is the ease of development?

  • How fast can new team members understand what's going on?

  • How fast can you figure out where something is broken?

Sometimes a seemingly simple feature request comes through that looks like this:

If X happens do Y, but only if state looks like Z.

Since you want to maintain your code decoupled between react (view) and redux (data management), simple requests like this end up seeming more difficult than they should be.

A good example is client-side tracking.

At some point you'll want, nay, need to track when a user clicks on something and pass how many items are in the display or other information about the current state.

The first pass at this was an analytics middleware.

Things were based on a property aptly named "analytics".

So the action looked like this:

return {
  types: [ MARK_READ, MARK_READ_SUCCESS, MARK_READ_ERROR ],

  callAPI: () => Api.updateTodos({
    ids,
    isRead: true 
  }),

  analytics: {
    [MARK_READ_SUCCESS] : {
      idsCount: ids.length,
      isSuccess: true
    },
    [MARK_READ_ERROR] : {
      idsCount: ids.length,
      isSuccess: false
    }
  }
}


So far... not horrible.

Depending on the event type (success/error), we can track different things.

But a new requirement comes in and... You won't believe what happened next.

 

If it's successful, we also need to redirect the user to a different section.

return {
  types: [ MARK_READ, MARK_READ_SUCCESS, MARK_READ_ERROR ],

  callAPI: () => Api.updateTodos({
    ids,
    isRead: true 
  }),

  analytics: {
    [MARK_READ_SUCCESS] : {
      idsCount: ids.length,
      isSuccess: true
    },
    [MARK_READ_ERROR] : {
      idsCount: ids.length,
      isSuccess: false
    }
  },

  redirect: ({ state }) {

  }

}


We made another middleware that looked for a specific property.

We sure did.

But that won't scale.

We need something more generic that can scale for different use cases.

A Super-Handy Generic Middleware

Scenario:

if I'm filtering a list of todos  
  If I mark all as read   
      clear the filter   
  If I mark only some of them as read 
     leave the filter on

OnError show an error notification

Rules:

  • This logic shouldn't live in the view.

  • This is based on the action of marking a todo.
    If I add a different version of this component, an A/B test, for instance, the state should be updated, and everything should just work.

component.react.js

handleMarkRead (ids) {
    dispatch(markReadTodos(ids));
}


actions.js

import { clearTodosFilter } from '../filterActions';
import {
    MARK_READ,
    MARK_READ_SUCCESS,
    MARK_READ_ERROR
} from '../constants'

export function updateItem (ids) {
  return {
    types: [ MARK_READ, MARK_READ_SUCCESS, MARK_READ_ERROR ],

    callAPI: () => Api.updateTodos({
        ids,
        isRead: true 
    }),

    effect ({ dispatch, state, type }) {
      if (type === MARK_READ_ERROR) {


        dispatch(showErrNotification(
         'There was an error updating your todos'
         ));  
      }

      if (type === MARK_READ_SUCCESS) {
        const { todosById } = this.state;
        let hasReadAll = true;
        for ( const id in todosById) {
          if (!todosById[id].isRead) {
            hasReadAll = false;
            break;
          }
        }

        if (hasReadAll) {
          dispatch(clearTodosFilter());
        }
      }
    },
  };
}


Scenario:

When the user updates an item, show an error notification if it fails.

If it succeeds, show a different notification, but only if the item is FOO or some other state changed.

.

Item.react.js

handleUpdateItem (item) {
  dispatch(updateItem({ 
    item
  });
}

ItemActions.js
  1. Update Item call will happen.

  2. App State will be updated.

  3. If there's an error:
    – We'll dispatch an error notification.

  4. If it was successful and the current changes count (based on current state) is not valid, we'll dispatch a different notification.

import { showErrNotifcation } from '../notificationActions';
import {
    UPDATE_ITEM,
    UPDATE_ITEM_SUCCESS,
    UPDATE_ITEM_ERROR,
    MAX_CHANGES
} from '../constants'

export function updateItem ({ item, prevItem }) {
  return {
    types: [ UPDATE_ITEM, UPDATE_ITEM_SUCCESS, UPDATE_ITEM_ERROR ],

    callAPI: () => Api.updateItem(item),

    effect ({ dispatch, state, type }) {
      if (type === UPDATE_ITEM_ERROR) {


        dispatch(showErrNotification('Error updating item'));  
      }

      if (type === UPDATE_ITEM_SUCCESS) {
        const { itemsById } = this.state;

        if (itemsById[item.id].changes === MAX_CHANGES) {
          dispatch(showNotification(
            `You can no longer alter this item`
           )); 
        }
      }
    },
  };
}


And the Middleware...

effectsMiddleware.js

export default function effectsMiddleware ({ dispatch, getState }) {
  return next => action => {
    const nextValue = next(action);




    if (!action.effect) {
      if (!isFunction(action.effect)) {
        throw new Error('Expected effect to be a function');
      }




      action.effect({
        dispatch : dispatch,
        state    : getState(),
        type     : action.type,
      });

      delete action.effect;
    }

    return nextValue;
  };
}

You'll want to make sure it is placed after your other custom middlewares, however.
const middleware = process.env.NODE_ENV === 'production' ?
  [ thunk,  callAPIMiddleware, effectsMiddleware ] :
  [ thunk, callAPIMiddleware, effectsMiddleware, logger() ];


Where We Ended Up . . .

  • The logic for sub-actions is with the actions code.

  • Since we're dispatching other sub-actions, you can grep for action names, and they'll be displayed on your console for debugging.

  • You'll be able to follow a flow for actions happening in your code in the console.

Middleware Console (video game CLI) Pass (software) Requests POST (HTTP) Property (programming) app Ease (programming language) Scaling (geometry)

Published at DZone with permission of Cristiano Oliveira, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How to Build Slack App for Audit Requests
  • Unleashing the Power of GPT: A Comprehensive Guide To Implementing OpenAI’s GPT in ReactJS
  • Streamlining Your Workflow With the Jenkins HTTP Request Plugin: A Guide to Replacing CURL in Scripts
  • Auto-Scaling a Spring Boot Native App With Nomad

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: