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

jantix

v0.3.0

Published

A fully-featured TypeScript library defining a self-contained 100% type-safe Redux architecture.

Downloads

38

Readme

Jantix

NPM version badge for jantix Last commit badge

Jantix is a fully-featured TypeScript library that defines a tiny (837 bytes gzip'd), self-contained, 100% type-safe Redux architecture. It allows for stores following a common pattern to be easily defined, merged, and used through the native Redux interface with type safety and consistency guarantees automatically applied.

In addition to enforcing a typed interface for interacting with the created store, it also provides many useful guarantees about the validity of the provided actions/reducers by preventing mismatches between action types, invalid actions, and mal-formed reducers all at compile-time.

Installation

yarn add jantix

or

npm install --save jantix

Design

Jantix works on the principle of coupling action creators, actions/types, reducers, and state together at both the type and structural level. One of the biggest annoyances with traditional Redux setups is the large spread in places where the various components of the Redux store are defined. Your action creators often go in one file, your reducers in another, your action type constants in another, etc. In addition to fragmenting your thought process when dealing with the code and making it easier for bugs/inconsistencies, it also makes creating manually established types for all of the different pieces more difficult.

Jantix represents an entire segment of the Redux store as a structure combining each of these pieces together at the code level. The hierarchy looks like this:

           +-------------------------------------------------+
           |                                                 |
           |                Global Redux Store               |
           |                                                 |
           |   Key A              Key B              Key C   |
           |                                                 |
           |                                                 |
           +------------------------+------------------------+
           |                        |                        |
           |                        |                        |
           |                        |                        |
           |                        |                        |
     +-----v-----+            +-----v-----+            +-----v-----+
     |           |            |           |            |           |
     |  State A  |            |  State B  |            |  State C  |
     |     +     |            |     +     |            |     +     |
     |  Reducer  |            |  Reducer  |            |  Reducer  |
     |           |            |           |            |           |
     +-----------+            +-----------+            +-----------+
     |           |            |           |            |           |
     |           |            |           |            |           |
     |           |            |           |            |           |
+----v----+ +----v----+  +----v----+ +----v----+  +----v----+ +----v----+
|         | |         |  |         | |         |  |         | |         |
| Action  | | Action  |  | Action  | | Action  |  | Action  | | Action  |
| Creator | | Creator |  | Creator | | Creator |  | Creator | | Creator |
|         | |         |  |         | |         |  |         | |         |
| Sub-    | | Sub-    |  | Sub-    | | Sub-    |  | Sub-    | | Sub-    |
| Reducer | | Reducer |  | Reducer | | Reducer |  | Reducer | | Reducer |
|         | |         |  |         | |         |  |         | |         |
+---------+ +---------+  +---------+ +---------+  +---------+ +---------+

The bottom-most boxes are called "Action Groups" in Jantix. The middle boxes are called "modules," and the top level is the global Redux store that is generated. An action group is essentially a mini-reducer that contains a single action and a single reducer for handling that action. Multiple of these action groups are combined into each module, each of which has its own state which is shared between each of the action groups. Finally, multiple modules are combined into a single Redux store with each module having its own unique key.

Here is an example of a complete Jantix architecture consisting of a single action group and store:

import { buildModule, buildStore, buildActionGroup } from 'jantix';

type KVStoreState = { [key: string]: string | undefined };

const actionGroups = {
  SET: buildActionGroup({
    actionCreator: (key: string, value: string) => ({ type: 'SET', key, value }),
    // The type of the second action argument are automatically inferred + type-checked against the action creator above
    subReducer: (state: KVStoreState, { key, value }) => ({ ...state, [key]: value }),
  }),
};

// The second type argument is necessary due to the internal tricks used to perform static type assertions on the
// structure of the action groups + module definition
const kvStoreModule = buildModule<KVStoreState, typeof actionGroups>({}, actionGroups);

const storeDefinition = {
  kvStore: kvStoreModule,
};

// This internally calls `combineReducers` and produces a new Redux store as well as strongly-typed methods for
// interacting with it and integrating it into React.
const { actionCreators, dispatch, getState } = buildStore<typeof storeDefinition>(storeDefinition);

The end result of this is a fully-functional Redux store that can be interacted with like any other. It will also generate wrapped store, actionCreators, dispatch, getState, and useSelector variables that are 100% typed and statically validated. You can export them out of the file you created your store in and make use of them in the exact same way you would with normal, un-wrapped Redux.

Jantix does a lot of helpful things in between these few steps. First of all, it ensures that you have a 100% typed Redux architecture. This is extremely valuable in and of itself; all of the boilerplate is taken care of internally, and every single part is tied up with the same overarching, centralized types. Editor type completion is integrated perfectly as well:

Adding to that, it statically verifies that each action type/action creator has a handler for its action in the reducer. If you were to set the type prop of the created object to a different value than the key of the actionsGroup object to which it belongs, a type-level error will be triggered.

Due to the way that action groups are designed, you also get exhaustiveness checking for actions built-in (it's impossible to have an action/action creator that doesn't have a corresponding handler/sub-reducer). In addition, it also creates a dispatch function that only accepts known action types. Trying to do dispatch({ type: 'UNHANDLED' }) will also trigger a type-level error.

Integrating with External Redux Modules

Some libraries integrate directly into redux, such as connected-react-router, and create their own store keys, actions, and reducers. Since these don't fit into the siloed Jantix architecture, they must be represented specially. Jantix supports this with the addition of two additional optional type arguments on the buildStore function: CustomReducers and CustomState. An example of how to use this can be found in examples/basic.ts.

Usage / Example

Check out examples/basic.ts for a more complete example of how to use jantix.

To see a larger-scale project that makes use of Jantix, check out my Spotify Stats Website Repo.