Wondering what’s next for npm?Check out our public roadmap! »

    redux-zero
    TypeScript icon, indicating that this package has built-in type declarations

    5.1.7 • Public • Published

    redux zero logo

    A lightweight state container based on Redux

    Read the intro blog post

    {count}

    codacy build npm downloads license dependencies

    Table of Contents

    Installation

    To install the stable version:

    npm i redux-zero
    

    This assumes that you’re using npm with a module bundler like webpack

    How

    ES2015+:

    import createStore from "redux-zero";
    import { Provider, connect } from "redux-zero/react";

    TypeScript:

    import * as createStore from "redux-zero";
    import { Provider, connect } from "redux-zero/react";

    CommonJS:

    const createStore = require("redux-zero");
    const { Provider, connect } = require("redux-zero/react");

    UMD:

    <!-- the store -->
    <script src="https://unpkg.com/redux-zero/dist/redux-zero.min.js"></script>
     
    <!-- for react -->
    <script src="https://unpkg.com/redux-zero/react/index.min.js"></script>
     
    <!-- for preact -->
    <script src="https://unpkg.com/redux-zero/preact/index.min.js"></script>
     
    <!-- for vue -->
    <script src="https://unpkg.com/redux-zero/vue/index.min.js"></script>
     
    <!-- for svelte -->
    <script src="https://unpkg.com/redux-zero/svelte/index.min.js"></script>

    Example

    Let's make an increment/decrement simple application with React:

    First, create your store. This is where your application state will live:

    /* store.js */
    import createStore from "redux-zero";
     
    const initialState = { count: 1 };
    const store = createStore(initialState);
     
    export default store;

    Then, create your actions. This is where you change the state from your store:

    /* actions.js */
    const actions = store => ({
      increment: state => ({ count: state.count + 1 }),
      decrement: state => ({ count: state.count - 1 })
    });
     
    export default actions;

    By the way, because the actions are bound to the store, they are just pure functions :)

    Now create your component. With Redux Zero your component can focus 100% on the UI and just call the actions that will automatically update the state:

    /* Counter.js */
    import React from "react";
    import { connect } from "redux-zero/react";
     
    import actions from "./actions";
     
    const mapToProps = ({ count }) => ({ count });
     
    export default connect(
      mapToProps,
      actions
    )(({ count, increment, decrement }) => (
      <div>
        <h1>{count}</h1>
        <div>
          <button onClick={decrement}>decrement</button>
          <button onClick={increment}>increment</button>
        </div>
      </div>
    ));

    Last but not least, plug the whole thing in your index file:

    /* index.js */
    import React from "react";
    import { render } from "react-dom";
    import { Provider } from "redux-zero/react";
     
    import store from "./store";
     
    import Counter from "./Counter";
     
    const App = () => (
      <Provider store={store}>
        <Counter />
      </Provider>
    );
     
    render(<App />, document.getElementById("root"));

    Here's the full version: https://codesandbox.io/s/n5orzr5mxj

    By the way, you can also reset the state of the store anytime by simply doing this:

    import store from "./store";
     
    store.reset();

    More examples

    Actions

    There are three gotchas with Redux Zero's actions:

    • Passing arguments
    • Combining actions
    • Binding actions outside your application scope

    Passing arguments

    Here's how you can pass arguments to actions:

    const Component = ({ count, incrementOf }) => (
      <h1 onClick={() => incrementOf(10)}>{count}</h1>
    );
     
    const mapToProps = ({ count }) => ({ count });
     
    const actions = store => ({
      incrementOf: (state, value) => ({ count: state.count + value })
    });
     
    const ConnectedComponent = connect(
      mapToProps,
      actions
    )(Component);
     
    const App = () => (
      <Provider store={store}>
        <ConnectedComponent />
      </Provider>
    );

    Access props in actions

    The initial component props are passed to the actions creator.

    const Component = ({ count, increment }) => (
      <h1 onClick={() => increment()}>{count}</h1>
    );
     
    const mapToProps = ({ count }) => ({ count });
     
    const actions = (store, ownProps) => ({
      increment: state => ({ count: state.count + ownProps.value })
    });
     
    const ConnectedComponent = connect(
      mapToProps,
      actions
    )(Component);
     
    const App = () => (
      <Provider store={store}>
        <ConnectedComponent value={10} />
      </Provider>
    );

    Combining actions

    There's an utility function to combine actions on Redux Zero:

    import { connect } from "redux-zero/react";
    import { combineActions } from "redux-zero/utils";
     
    import Component from "./Component";
    import firstActions from "../../actions/firstActions";
    import secondActions from "../../actions/secondActions";
     
    export default connect(
      ({ params, moreParams }) => ({ params, moreParams }),
      combineActions(firstActions, secondActions)
    )(Component);

    Binding actions outside your application scope

    If you need to bind the actions to an external listener outside the application scope, here's a simple way to do it:

    On this example we listen to push notifications that sends data to our React Native app.

    import firebase from "react-native-firebase";
    import { bindActions } from "redux-zero/utils";
    import store from "../store";
    import actions from "../actions";
     
    const messaging = firebase.messaging();
    const boundActions = bindActions(actions, store);
     
    messaging.onMessage(payload => {
      boundActions.saveMessage(payload);
    });

    Async

    Async actions in Redux Zero are almost as simple as sync ones. Here's an example:

    const mapActions = ({ setState }) => ({
      getTodos() {
        setState({ loading: true });
     
        return client
          .get("/todos")
          .then(payload => ({ payload, loading: false }))
          .catch(error => ({ error, loading: false }));
      }
    });

    They're still pure functions. You'll need to invoke setState if you have a loading status. But at the end, it's the same, just return whatever the updated state that you want.

    And here's how easy it is to test this:

    describe("todo actions", () => {
      let actions, store, listener, unsubscribe;
      beforeEach(() => {
        store = createStore();
        actions = getActions(store);
        listener = jest.fn();
        unsubscribe = store.subscribe(listener);
      });
     
      it("should fetch todos", () => {
        nock("http://someapi.com/")
          .get("/todos")
          .reply(200, { id: 1, title: "test stuff" });
     
        return actions.getTodos().then(() => {
          const [LOADING_STATE, SUCCESS_STATE] = listener.mock.calls.map(
            ([call]) => call
          );
     
          expect(LOADING_STATE.loading).toBe(true);
          expect(SUCCESS_STATE.payload).toEqual({ id: 1, title: "test stuff" });
          expect(SUCCESS_STATE.loading).toBe(false);
        });
      });
    });

    Middleware

    The method signature for the middleware was inspired by redux. The main difference is that action is just a function:

    /* store.js */
    import createStore from "redux-zero";
    import { applyMiddleware } from "redux-zero/middleware";
     
    const logger = store => (next, args) => action => {
      console.log("current state", store.getState());
      console.log("action", action.name, ...args);
      return next(action);
    };
     
    const initialState = { count: 1 };
    const middlewares = applyMiddleware(logger, anotherMiddleware);
     
    const store = createStore(initialState, middlewares);
     
    export default store;

    DevTools

    You can setup DevTools middleware in store.js to connect with Redux DevTools and inspect states in the store.

    /* store.js */
    import createStore from "redux-zero";
    import { applyMiddleware } from "redux-zero/middleware";
    import { connect } from "redux-zero/devtools";
     
    const initialState = { count: 1 };
    const middlewares = connect ? applyMiddleware(connect(initialState)) : [];
    const store = createStore(initialState, middlewares);
     
    export default store;

    Also, these are unofficial tools, maintained by the community:

    TypeScript

    You can use the BoundActions type to write your React component props in a type safe way. Example:

    import { BoundActions } from "redux-zero/types/Actions";
     
    interface State {
      loading: boolean;
    }
     
    const actions = (store, ownProps) => ({
      setLoading: (state, loading: boolean) => ({ loading })
    });
     
    interface ComponentProps {
      value: string;
    }
     
    interface StoreProps {
      loading: boolean;
    }
     
    type Props = ComponentProps & StoreProps & BoundActions<State, typeof actions>
     
    class Component = (propsProps) => (
      <h1 onClick={() => props.setLoading(!props.loading)}>{props.value}</h1>
    );
     
    const mapToProps = (state: State): StoreProps => ({ loading: state.loading });
     
    const ConnectedComponent = connect<State, ComponentProps>(
      mapToProps,
      actions
    )(Component);
     
    const App = () => (
      <Provider store={store}>
        <ConnectedComponent value={10} />
      </Provider>
    );

    By doing this, TypeScript will know the available actions and their types available on the component's props. For example, you will get a compiler error if you call props.setLoding (that action doesn't exist), or if you call it with incorrect argument types, like props.setLoading(123).

    Inspiration

    Redux Zero was based on this gist by @developit

    Roadmap

    • Make sure all bindings are working for latest versions of React, Vue, Preact and Svelte
    • Add time travel

    Help is needed for both of these

    Docs

    Install

    npm i redux-zero

    DownloadsWeekly Downloads

    2,069

    Version

    5.1.7

    License

    MIT

    Unpacked Size

    80.9 kB

    Total Files

    55

    Last publish

    Collaborators

    • avatar
    • avatar