Skip to content

Conversation

jchansen
Copy link
Contributor

@jchansen jchansen commented May 14, 2017

This PR simplifies the interface for extending and overriding the blueprints and mapping behavior used by lore-hook-connect. It also adds two new blueprints; all and byCid.

The all blueprint is extremely helpful for finishing the Next Steps section of the Quickstart, as it provides a simple way to retrieving the new tweets.

The all Blueprint

The all blueprint is intended to provide a way to filter and sort reducer data when the intent is to NOT make a server call. For example, in the Quickstart we fetch a page of tweets, and sometimes create new tweets. In this scenario, we know the new tweets are in the store, and we want to display them on top of the paginated tweets.

Before that, accomplishing that was not straight forward, as it required you having to access the Redux Store directly, getting all the data from the byCid reducer, and then filtering and sorting it to get what you want.

Now you can skip that process, and provide the filter and sortBy criteria directly to the getState call like this:

import moment from 'moment';
const timestamp = moment().toISOString();

@lore.connect(function(getState, props) {
  return {
    newTweets: getState('tweet.all', {
      where: function(tweet) {
        return !tweet.id || moment(tweet.data.createdAt).diff(timestamp) > 0
      },
      sortBy: function(tweet) {
        return -moment(tweet.data.createdAt).unix();
      }
    })
  }
})

The getState call above will return all tweets created after the timestamp or that don't have an id (implying that were optimistically created, and should be displayed). The sortBy function then orders the tweets by their createdAt date.

The byCid Blueprint

Not sure how useful this blueprint is in practice, but occasionally you do need to find data by the cid value, when you're waiting or looking for confirmation of when data is confirmed to be created by the server.

If it makes sense to use a lore.connect call to accomplish that, now you can.

@lore.connect(function(getState, props) {
  return {
    tweet: getState('tweet.byCid', {
      cid: 'c2'
    })
  }
})

The above getState call will look through the tweet.byCid reducer and return the tweet with the cid of c2. If no tweet exists with the matching cid, the call will return undefined.

The Interface for Extending/Overriding Blueprints and Mapping

To illustrate the interface, let's take a look at how you could add the all blueprint yourself.

First, you need to define the blueprint in the config/connect.js file like this:

// config/connect.js
var _ = require('lodash');

module.exports = {

  blueprints: {
    all: {
      defaults: {
        where: function(model) {
          return true;
        },
        sortBy: function(model) {
          return true;
        }
      },

      verifyParams: function(params) {
        if (!_.isFunction(params.where)) {
          throw new Error("The 'where' field must be a function");
        }

        if (!_.isFunction(params.sortBy)) {
          throw new Error("The 'sortBy' field must be a function");
        }
      },

      getAction: function(actions) {
        // no op
      },

      getPayload: function (reducerState, params) {
        var data = _
          .chain(reducerState)
          .transform(function(result, model) {
            result.push(model);
          }, [])
          .filter(params.where)
          .sortBy(params.sortBy)
          .value();

        return {
          state: 'RESOLVED',
          data: data
        };
      }
    }
  }

};

This blueprint defines two default parameters, where and sortBy, has no action, and iterates through the provided reducerState, filtering and sorting the results, which are then returned in a data structure with the state always set to RESOLVED (since it makes no server calls, the state is never transient).

Next, you need to tell lore how to use it. To do that, we need to define a reducerActionMap, and we can use the new * syntax to say "this mapping applies to all models".

// config/connect.js
module.exports = {
  blueprints: {
    all: {
      // ... definition
    }
  },

  reducerActionMap: {
    '*.all': {
      action: null,
      reducer: '*.byCid',
      blueprint: 'filter'
    }
  }

};

That's it! With those two changes, you can easily extend and override the behavior of the lore.connect function and associated getState calls.

@jchansen jchansen force-pushed the simplify-lore-hook-connect-interface branch from c71c56c to 7b7318c Compare May 14, 2017 13:54
@jchansen jchansen merged commit a2a1180 into master May 14, 2017
@jchansen jchansen deleted the simplify-lore-hook-connect-interface branch May 14, 2017 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant