npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

uptrend-redux-modules

v0.23.0

Published

Redux Module (redux + redux-saga + redux-saga-thunk) for requesting resources from API and storing response data into entities if provided a normalizr schema.

Downloads

27

Readme

Build Status Code Coverage version downloads MIT License

All Contributors PRs Welcome Code of Conduct

Watch on GitHub Star on GitHub Tweet

The problem

At Uptrend we enjoy building React applications and have had success using redux + normalizr to manage state and redux-saga + redux-saga-thunk to orchestrate application side effects (i.e. asynchronous things like data fetching). Code is easy to understand and typically works as expected but someone could have a criticism about the amount of ceremony and boilerplate required.

Typically, whenever adding a new entity to an app it required us to write reducers, actions, sagas, schemas, selectors, and container components to get basic CRUD functionality.

This solution

Create a concise and straightforward way to make HTTP requests that normalize response handling including normalization of response data into index entities in the redux store. To get CRUD functionality for a new entity, you add a normalizr schema and use the provided actions and selectors provided by URM (uptrend-redux-modules). URM also provides render prop React components that simplify and reduce the amount of code needed.

Below are code examples to highlight what using URM resource and entities looks like:

URM Resource & Entities Graph

ResourceDetailLoader Component

const OrgDetailAutoLoader = ({orgId}) => (
  <ResourceDetailLoader resource="org" resourceId={orgId} autoLoad>
    {({status, result, onEventLoadResource}) => (
      <div>
        <pre>{'autoLoad={true}'}</pre>

        <button onClick={onEventLoadResource} disabled={status.loading}>
          Load Resource
        </button>

        {status.initial && <span className="label label-default">initial</span>}
        {status.loading && <span className="label label-primary">loading</span>}
        {status.error && <span className="label label-danger">error</span>}
        {status.success && <span className="label label-success">success</span>}

        {status.loading ? (
          <h5>Loading...</h5>
        ) : (
          result && (
            <div>
              <div>
                Org ID: <code>{result.id}</code>
              </div>
              <div>
                Active: <code>{result.active ? 'Yes' : 'No'}</code>
              </div>
            </div>
          )
        )}
      </div>
    )}
  </ResourceDetailLoader>
)

ResourceListLoader Component

const OrgListLoader = () => (
  <ResourceListLoader resource="org">
    {({status, result, onEventLoadResource}) => (
      <div>
        <div>
          <pre>{'autoLoad={false}'}</pre>
          <pre>{JSON.stringify(status, null, 2)}</pre>
        </div>

        <button onClick={onEventLoadResource} disabled={status.loading}>
          Load Resource
        </button>

        {status.initial && <span className="label label-default">initial</span>}
        {status.loading && <span className="label label-primary">loading</span>}
        {status.error && <span className="label label-danger">error</span>}
        {status.success && <span className="label label-success">success</span>}

        {status.loading ? (
          <h5>Loading...</h5>
        ) : (
          result &&
          result.map(org => (
            <div key={org.id}>
              <span>
                Org ID: <code>{org.id}</code>
              </span>
              <span>
                Active: <code>{org.active ? 'Yes' : 'No'}</code>
              </span>
            </div>
          ))
        )}
      </div>
    )}
  </ResourceListLoader>
)

Using Resource Redux-Saga-Thunk Style

Resource actions provide a promise based interface that redux-saga-thunk allows. Below shows how a resource can use without using selectors. This is nice when you need resource data to save locally.

import React from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'

const mapDispatchToProps = dispatch => ({
  loadGroups: () =>
    dispatch(resourceListReadRequest('group', {active: true}, 'group')),
})

class GroupListContainer extends React.Component {
  state = {
    loading: false,
    groupList: null,
  }

  componentDidMount() {
    this.loadGroups()
  }

  loadGroups() {
    this.setState({loading: true})
    this.props.loadGroups().then(this.handleLoadSuccess, this.handleLoadFail)
  }

  handleLoadFail = error => {
    this.setState({loading: false, error})
  }

  handleLoadSuccess = ({entities}) => {
    this.setState({loading: false, groupList: entities})
  }

  render() {
    const {loading, groupList} = this.state

    if (loading) return <div>Loading...</div>

    return (
      <ul>{groupList.map(group => <li key={group.id}>{group.name}</li>)}</ul>
    )
  }
}

GroupListContainer.propTypes = {
  fetchTripGroupList: PropTypes.func.isRequired,
}

export default connect(null, mapDispatchToProps)(GroupListContainer)

Redux Modules

There

// TODO

Table of Contents

Installation

This module is distributed via npm which is bundled with node and should be installed as one of your project's dependencies:

yarn add uptrend-redux-modules

Example Project Usage

Below is an example of how one may set it up in a react app using the resource and entities redux-modules.

Do note there are many ways you could organize your project and this example is not strict guidelines by any means.

Resource & Entities

  • src/store/modules/resource/index.js

    // - src/store/modules/resource/index.js
    import {createResource} from 'uptrend-redux-modules'
    
    // createResource(...) => { actions, reducers, sagas, selectors }
    export default createResource()
  • src/store/modules/entities/index.js

    // - src/store/modules/entities/index.js
    import {createEntities} from 'uptrend-redux-modules'
    import schemas from './schemas'
    
    // createEntities(...) => { actions, middleware, reducers, sagas, selectors }
    export default createEntities({schemas})
  • src/store/modules/entities/schema.js

    // - src/store/modules/entities/schemas.js
    import {schema} from 'normalizr'
    
    export const user = new schema.Entity('users')
    export const team = new schema.Entity('teams', {owner: user, members: [user]})
  • src/store/actions.js

    // - src/store/actions.js
    import {actions as entities} from 'src/store/modules/entities';
    import {actions as resource} from 'src/store/modules/resource';
    
    export {
      ...entities,
      ...resource,
    }
  • src/store/middlewares.js

    // - src/store/middlewares.js
    import {middleware as entities} from 'src/store/modules/entities'
    
    export default [
      // redux-modules middlewares
      entities,
    ]
  • src/store/reducers.js

    // - src/store/reducer.js
    import {combineReducers} from 'redux'
    
    import {reducer as entities} from 'src/store/modules/entities'
    import {reducer as resource} from 'src/store/modules/resource'
    
    export default combineReducers({
      entities,
      resource,
    })
  • src/store/sagas.js

    // - src/store/sagas.js
    import {sagas as entities} from 'src/store/modules/entities'
    import {sagas as resource} from 'src/store/modules/resource'
    
    // single entry point to start all Sagas at once
    export default function*(services = {}) {
      try {
        yield all([
          // app specific sagas
          example(services),
    
          // redux-modules sagas
          entities(services),
          resource(services),
        ])
      } catch (error) {
        console.error('ROOT SAGA ERROR!!!', error)
        console.trace()
      }
    }
  • src/store/selectors.js

    // - src/store/selectors.js
    import {selectors as fromEntities} from 'src/store/modules/entities'
    import {selectors as fromResource} from 'src/store/modules/resource'
    
    export {fromEntities, fromResource}

Usage

// TODO

Inspiration

Organizing actions, reducers, selectors, sagas, etc. into a module is based on redux-modules from Diego Haz.

The resource and entities modules specifically are modified version of those found in redux-modules and ARc.js.

Other Solutions

I'm not aware of any, if you are please make a pull request and add it here!

Contributors

| Brandon Orther💻 🚇 ⚠️ 💡 | Dylan Foster🐛 🤔 | | :---: | :---: |

Thanks goes to these people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!

LICENSE

MIT