What is defensive programming?

Defensive programming is a design pattern intended to ensure that software continues to function under unforseen circumstances. At its core, it is built around the concept of making your code and its contracts predictable. This practice is particularly of value when having to interface with legacy systems or third party APIs where consistency is not only not guaranteed but unlikely.

In a typical React/Redux application we will use the react-redux library to create container components that act as data entry points in our app. There are varying opinions on where and when to use containers but for the sake of this discussion we're going to operate under the idea that containers are not solely top level components nor are they treated differently than presentational components. One of our goals being that we will write as many pure components as possible - a pure component is one that returns the same output given the same props.

How can we do this in React with Redux?

To accomplish this we are going to focus on how data is used in our application and where any business logic is performed. This is where the selector pattern comes into play. We will use selectors to compute derived data and compose them defensively to produce information to our views that is predictable. Essentially freeing our component event lifecycle and reducers from having to perform these actions and therefore simplifying our components making them more modular, dependable, testable, and predictable.

To get started we will install a library called reselect. It's touted as a simple "selector" library for Redux inspired by getters in NuclearJS. It's straight forward to use and will allow us to easily create memoized selectors. The memoization of selectors helps us reduce the amount of unecessary recalculations by only recalculating when the value used is changed but not when changes occur in other, unrelated parts of the state tree.

To start, let's say we have a state tree that contains a list of movies. It might look something like this inside a media reducer:

{
    ...
    movies: [
        {
            name: "Monty Python and the Holy Grail"
            year: 1975,
            description: "King Arthur and his knights embark on a low-budget search for the grail",
            reviews: [
                {
                    title: "A comedic send-up of the grim circumstances of the Middle Ages",
                    stars: 4
                }
                ...
            ]
        }
        ...
    ]
}

Now, we have some requirements for our UI that indicates what kind of information we want to display. Let's say for now, our UI just wants to display a list of movies with their name and average rating. The data we have doesn't include a ratings aggregate so we will have to calculate it ourselves. In this scenario you might simply grab the movies off the state in mapStateToProps like so and calculate in the render or mapStateToProps function:

const mapStateToProps = (state): IStateProps => {
    return {
        movies: state.media.movies
    }
}

This has a few flaws, namely, it assumes that state.media.movies isn't null or we have to check it in the render/mapStateToProps and that we will need all of the data from the movies object.

Instead, let's compose this as a series of selectors and add some defensive coding to ensure that our UI gets what it expects every time, an array of data about the movie OR an empty array.

const moviesSelector = state => 
    Array.isArray(state.media.movies)
    ? state.media.movies
    : [];

While this is verbose, I have opted against a soft falsey compare and what to test directly that state.media.movies is in fact an array of some kind and if it isn't we will return an empty array to the next selector.

In the next selector we will map our movies to a model that more clearly defines the requirements of our UI.

const moviesSummarySelector = createSelector([
    moviesSelector
], (movies): MovieSummary => {
    return movies.map(movie => {
        const {
            name,
            reviews,
        } = movie;
        return {
            name: typeof name !== "string"
            ? ""
            : name,
            rating: Array.isArray(reviews)
            ? reviews.reduce((prev, curr) => current += prev, 0) / reviews.length
            : 0
        }
    })
})

In here we can be confident that, because we are using the moviesSelector, the movies result will always be an array and as a result we have an easily testable piece of business logic to calculate the rating. We ensure that the object passed to the component is exactly as we want and don't leave it up for future developers to use the API data result directly from state often leading null or undefined errors in our render functions.

Now in map state to props we can use the selector:

const mapStateToProps = (state: IRootState): IStateProps => {
    return {
        movies: moviesSummarySelector(state),
    }
}

And in our component we have a more confidence in our props returning the type of information we need with safe default values our UI can use without having to perform complex render logic.

Summary

This practice of memoizing your selectors and writing defensively inside of them to produce consistent results will help new developers in the codebase form a clearer understanding of the business logic and UI's data requirements while removing the need to trust that the data itself is always going to be consistent with our original expectations. Safe guarding your props will reduce regressions and give you a safer fallback in case the data is malformed or unexpected. This practice isn't just for collections of objects to be mapped but even if you need to select a single field from a complex object. It is safer to memoize access to the nested object and select the field safely while performing null/undefined checks and providing a default value inside the selector.

In the end, we want to remove business logic from our render and mapStateToProp functions and move it into a place where we can easily write clear, defensive, code that is easily testable since after all, each selector itself should be a pure function. How defensive you write your code is entirely up to you and your application's requirements but centralizing where we perform our core logic is critical.