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

@brendanatme/redux-model

v2.0.0

Published

Max your Redux, scrap your boilerplate

Downloads

3

Readme

Redux-model

Max your redux, scrap your boilerplate

Redux-model is an opinionated layer built on top of Redux designed to cut out your boilerplate, encourage structure and best practices, and give powerful features from line zero. Throw out all those folders and files full of action types, action creators, selectors, effects, and reducers... and create simple models that do what you always want them to by default, while retaining all the flexibility of redux.

Installing

$ npm install --save @brendanatme/redux-model

Usage

// your model code
import { ReduxModel } from "@brendanatme/redux-model";
import * as api from "../my-api";

const productModel = new ReduxModel("products");

// define action creators as thunks or simple action creators
productModel.addAction("FetchById", (id) => async (dispatch) => {
  // leverage pre-existing built-in action creators
  dispatch(productModel.actions.BeginFetch());

  try {
    const item = await api.get(`/products/${id}`);

    // follow data-shape conventions to make your life easier,
    // and your code more consistent and readable
    dispatch(productModel.actions.FetchSuccess({ item }));
  } catch (e) {
    // redux-model gives you the tools that make following best-practices,
    // like proper network status handling,
    // quite trivial
    dispatch(productModel.actions.FetchFailure());
  }
});

export default productModel;
// your redux store startup code
import { configureStore } from "@brendanatme/redux-model";
import productModel from "../my/model/above";

const initStore = configureStore(
  {
    [productModel.key]: productModel.reducer,
  }
  // {}, // (optional) initial state
  // [], // (optional) redux middlewares
);

// call this function to initialize your store
export default initStore;
// your React component code
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import productModel from "../my/model/above";

export default ({ id }) => {
  const dispatch = useDispatch();

  // use built-in selectors to access data consistently
  // without needing to write or repeat procedural code
  const product = useSelector(productModel.selectors.item);
  const network = useSelector(productModel.selectors.network);

  useEffect(() => {
    dispatch(productModel.actions.FetchById(id));
  }, [product]);

  if (network.fetching) {
    return <Loader />;
  }

  if (network.failed) {
    return <Error />;
  }

  return <code>{JSON.stringify(product)}</code>;
};

Docs

Model-based approach

Models contain a key, a reducer, action types, action creators, selectors, and connectors. To instantiate a model, you only need to provide the key:

import { ReduxModel } from "@brendanatme/redux-model";

const productModel = new ReduxModel("products");

You can optionally add your own reducers:

import { ReduxModel } from "@brendanatme/redux-model";

const productModel = new ReduxModel("products", {
  reducers: {
    Foo: (state, action) => ({ ...state, ...action.payload }),
  },
});

You can access action types associated with your created reducers:

import { ReduxModel } from "@brendanatme/redux-model";

const productModel = new ReduxModel("products", {
  reducers: {
    Foo: (state, action) => ({ ...state, ...action.payload }),
  },
});

console.log(productModel.ActionTypes.Foo); // --> 'products/Foo'
console.log(productModel.ActionTypes.Bar); // --> undefined

You can also add your own action creators; types will be created for those as well.

import { ReduxModel } from "@brendanatme/redux-model";

const productModel = new ReduxModel("products", {
  reducers: {
    Foo: (state, action) => ({ ...state, ...action.payload }),
  },
});

productModel.addAction("Bar", async (dispatch) => {
  dispatch({ type: productModel.ActionTypes.Foo, payload: "baz" });
});

console.log(productModel.ActionTypes.Foo); // --> 'products/Foo'
console.log(productModel.ActionTypes.Bar); // --> 'products/Bar'

Next, we'll talk about the out-of-the-box actions redux-model provides, and the default, opinionated way, it handles state.

Opinionated state handling

Redux-model provides a number of actions to make commonplace tasks like fetching data and selecting items easy, consistent, and robust. Take fetching a resource, for example:

import { ReduxModel } from "@brendanatme/redux-model";
import * as api from "../my-api";

const productModel = new ReduxModel("products");

productModel.addAction("FetchById", (id) => async (dispatch) => {
  // initiate the BeginFetch() action
  // this sets our model's network state as such:
  // state.network = { failed: false, fetched: false, fetching: true }
  dispatch(productModel.actions.BeginFetch());

  try {
    const item = await api.get(`/products/${id}`);

    // when we successfully receive a response,
    // we can initiate the FetchSuccess() action
    // to a) update our network state: state.network = { failed: false, fetched: true, fetching: false }
    // and b) update our data state, using the 'item' key for a single resource or the 'items' key for an array
    dispatch(productModel.actions.FetchSuccess({ item }));
  } catch (e) {
    // if our request fails,
    // we can use the FetchFailure() built-in action creator
    // to update our network state to:
    // state.network = { failed: true, fetched: false, fetching: false }
    dispatch(productModel.actions.FetchFailure());
  }
});

// we now have access to the action type as well as the action creator:
console.log(productModel.ActionTypes.FetchById); // --> 'products/FetchById'
console.log(productModel.actions.FetchById); // --> function

// usage:
dispatch(productModel.actions.FetchById("f49wjto"));

Setting Model State

The best way to put data into your model state is by using the FetchSuccess method. This is because redux-model assumes you're loading your data from somewhere else, and so the FetchSuccess action will also automatically update your network status. The FetchSuccess method accepts an object for an argument with a key of item, or items, or both:

// ...

dispatch(productModel.actions.FetchSuccess({

  // for a singular resource
  item: {
    foo: 'bar',
  },

  // for an array of items
  items: [
    { id: 'f5soghl24', name: 'Product A' },
    { id: 'f5soghl25', name: 'Product B' },
  ],

});

Setting your data this way will give you easy access to it elsewhere with redux-model's built-in selectors, and will also give you added built-in functionality such as easily selecting an item from your array:

// get all items
const items = useSelector(productModel.selectors.items);

// ...

// set the selectedItem (using an id from objects within the 'items' array)
dispatch(productModel.actions.SelectItem("f5soghl25"));

// ...

// access the 'selectedItem'
const selectedItem = useSelector(productModel.selectors.selectedItem);

Escape Hatch

If you can't follow the convention, or just plain don't want to, you can use the Update() action creator as an escape hatch to set your state however you want:

// ...

// set a custom property
dispatch(productModel.actions.Update({ foo: "bar" }));

// ...

// get the model's entire state
const { foo } = useSelector(productModel.selectors.state);