Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify app code needed to load and access resource (and entities). #17

Closed
8 tasks done
orther opened this issue Apr 25, 2018 · 1 comment
Closed
8 tasks done
Assignees
Labels
enhancement New feature or request

Comments

@orther
Copy link
Member

orther commented Apr 25, 2018

Overview

There is quite a bit of boilerplate and ceremony involved to loading a resource,
checking it's status (pending/failed/done/etc) and then getting the response
using selectors (and pulling from entities if an entityType was set).
Traditionally using the redux-saga-thunk pattern is how we've avoided using
selectors. We can make this lib easier to use (less code, less thinking) and
more robust by providing mechanism(s) that do what the current boilerplate code
does. To support multiple use cases I believe it makes sense to provide these
helpers as action/selectors creators, render prop components (based off
selectors) and possible HoCs (higher order components).

Current Usage Example

Here is an example from one of our projects using resource/entities
redux-modules (not from uptrend-redux-modules but it has same API). Notice how
the request path member/search is used in the status selectors and in resource
getList selector. Also the entity type memberSearch is used in request action
and entities getList selector. Do note request path and entityType always need
to match like this:

import { isPending, hasFailed, isDone } from 'redux-saga-thunk';

import { fromEntities, fromResource } from 'rides/store/selectors';
import { modalShow, resourceListCreateRequest } from 'rides/store/actions';
import { MemberSearchPage } from 'rides/components';

const mapDispatchToProps = {
  search: searchParams =>
    resourceListCreateRequest(`member/search`, searchParams, 'memberSearch'),
  showCreateModal: () => modalShow('create-member'),
};

const mapStateToProps = state => ({
  memberListDone: isDone(state, `member/searchListCreate`),       // <-
  memberListFailed: hasFailed(state, 'member/searchListCreate'),  // <- notice the duplication
  memberListPending: isPending(state, 'member/searchListCreate'), // <-
  memberList: fromEntities.getList(
    state,
    'memberSearch', // <- entityType
    fromResource.getList(state, 'member/search'), 
  ),
});

Desired Usage Example

import {resourceCreate} from 'src/store/helpers'

const search = resourceListCreate(
  'member/search', // <- resource
  'memberSearch',  // <- entityType
);

const mapDispatchToProps = {
  search: params => search.action(params), // = resourceListCreateRequest(`user/member`, data, 'member)
};

const mapStateToProps = state => ({
  memberListDone: search.selectors.done(state),
  memberListFailed: search.selectors.rejected(state),
  memberListPending: search.selectors.pending(state),
  memberList: search.selectors.result(state),
});

What the search object looks like:

const search = {
  // The action will take whatever args the resource request action creator
  // takes besides `resource` and `entityType`. Most of the resource request
  // action creators take a single arg of: (data | needle | params)
  // `resourceUpdateRequest` takes two args: (needle, data)
  action, 
  
  selectors: {
  // redux-saga-thunk selectors
  pending,   // <- loading  (boolean)
  fulfilled, // <- success  (boolean)
  rejected,  // <- error    (boolean)
  done,      // <- finished (boolean)
  
  // returned data
  resource, // <- resource data (when normalized returns entity id(s))
  result,   // <- if the resource was normalized into entities this
            //    holds the list of entities, otherwise returns the
            //    resource data (non-normalized)
  }
}

Action & Selectors Creator

It should be pretty straight forward to create a helper that returns the request
action and selectors for accessing a resource like the above example.

  • src/store/helpers/index.js

    import {createResourceHelpers} from 'uptrend-redux-modules';
    import entities from 'sow/store/modules/entities';
    import resource from 'sow/store/modules/resource';
    
    const {
      // NOTE: one for each resource request action type
      resourceCreate,
      resourceDelete,
      resourceDetailRead, 
      resourceListCreate,
      resourceListRead,
      resourceUpdate, 
    } = createResourceHelpers({entities, resource});
    
    export {
      resourceCreate,
      resourceDelete,
      resourceDetailRead, 
      resourceListCreate,
      resourceListRead,
      resourceUpdate, 
    }
  • src/containers/MemberAddContainer.js

Render Prop Components

TBD - (Mostly copied from AECS)

Implementation Checklist

Upgrade redux-saga-thunk

  • Required for new status selectors (pending, fullfiled, rejected, done)

Helper Creators (one for each action creator below)

  • resourceCreateRequest
    • resourceCreateRequest = (resource, data, entityType) => ({
  • resourceListCreateRequest
    • resourceListCreateRequest = (resource, data, entityType) => ({
  • resourceUpdateRequest
    • resourceUpdateRequest = (resource, needle, data, entityType) => ({
  • resourceListReadRequest
    • resourceListReadRequest = (resource, params, entityType) => ({
  • resourceDeleteRequest
    • resourceDeleteRequest = (resource, needle, entityType) => ({
  • resourceDetailReadRequest
    • resourceDetailReadRequest = (resource, needle, entityType) => ({
  • resourceDeleteRequest
    • resourceDeleteRequest = (resource, needle, entityType) => ({
@orther orther added the enhancement New feature or request label Apr 25, 2018
@orther orther self-assigned this Apr 25, 2018
orther added a commit that referenced this issue Apr 25, 2018
This newest version adds ability to track an actions status with action and id
which makes tracking multiple requests to the same endpoint
concurrently possible.

The selectors have also been renamed. To support selector generation in the new
helpers outlined in #17 we need to expose these.
orther added a commit that referenced this issue Apr 26, 2018
* libs: Update redux-saga-thunk v0.7.1

This newest version adds ability to track an actions status with action and id
which makes tracking multiple requests to the same endpoint
concurrently possible.

The selectors have also been renamed. To support selector generation in the new
helpers outlined in #17 we need to expose these.

* refactor: Update resource success action payload #16

* refactor: Update resource sagas to provide new success payload format

* docs: Added Usage docks for resource action promise interface #16

* fix(resource): Delete Success saga call and action bug

* feat: Add createResourceHelper w/ only resouceCreate implemented

Need to write tests and implement the rest of these helper functions.

* feat(resource): WIP Add createResource w/ action test

Still need to add selectors tests and implement other resource request types.

* tests(helpers): WIP Add test for resourceCreate data selectors

* feat(helpers): Complete createResourceHelper
orther added a commit that referenced this issue Apr 26, 2018
There was an issue with a minor release, so this manual-releases.md
change is to release a new minor version.

Reference: #16 #17 #18
@orther
Copy link
Member Author

orther commented Apr 26, 2018

🎉 This issue has been resolved in version 0.5.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant