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

use-simple-state

v0.0.32

Published

A simple, dependency-free state manager for React using hooks.

Downloads

17

Readme

A simple, lightweight (3kb), dependency-free state manager for React, built using hooks.

Note: requires react and react-dom @ 16.7.0-alpha.2 or higher

Installation

Install the package using yarn or npm:

yarn add use-simple-state
npm install use-simple-state --save

Getting Started

Before we get started, we first need an initial state, as well as some actions and at least one reducer:

const initialState = { count: 0 };

const addOne = () => ({ type: 'ADD_ONE' });
const minusOne = () => ({ type: 'MINUS_ONE' });

const countReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_ONE':
      return { count: state.count + 1 };
    case 'MINUS_ONE':
      return { count: state.count - 1 };
}

Lastly, we simply import SimpleStateProvider, pass our reducers and initial state, then wrap our app's root component:

import React from 'react';
import { SimpleStateProvider } from 'use-simple-state';
import App from './App';

export default function Root () {
  return (
    <SimpleStateProvider initialState={initialState} reducers={[countReducer]}>
      <App />
    </SimpleStateProvider>
  );
}

And that's it.

Now whenever we want to access or update our state, we just import the useSimpleState hook:

import React from 'react';
import { useSimpleState } from 'use-simple-state';
import { addOne, minusOne } from './store';

export default function Counter () {
  const [state, dispatch] = useSimpleState();
  return (
    <>
      <h1>Count: {state.count}</h1>
      <button onClick={() => dispatch(addOne())}> +1 </button>
      <button onClick={() => dispatch(minusOne())}> -1 </button>
    </>
  );
}

Caveat

Hooks don't yet provide a way for us to bail out of rendering, although it currently looks as though this may be added in a future release (you can follow the dicussion here).

In the meantime I've provided a SimpleStateConsumer to consume our state using a consumer similar to the default one returned by React.createContext. This means our connected components won't re-render on every state change, but rather will only update when the specific part of the store they're subscribed to changes.

import { SimpleStateConsumer } from 'use-simple-state';

export default function Counter () {
  return (
    <SimpleStateConsumer mapState={({ count }) => ({ count })}>
      {({ state, dispatch }) => (
        <>
          <h1>Count: {state.count}</h1>
          <button onClick={() => dispatch(addOne())}> +1 </button>
          <button onClick={() => dispatch(minusOne())}> -1 </button>
        </>
      )}
    </SimpleStateConsumer>
  );
}

Async Actions

Comes with built-in support for asynchronous actions by providing an API similar to redux-thunk.

If a function is passed to dispatch it will be called with dispatch and state as parameters. This allows us to handle async tasks, like the following example of an action used to authenticate a user:

// Some synchronous actions
const logInRequest = () => ({ type: 'LOG_IN_REQUEST' });
const logInSuccess = ({ user }) => ({ type: 'LOG_IN_SUCCESS', payload: user });
const logInError = ({ error }) => ({ type: 'LOG_IN_ERROR', payload: error });

// Our asynchronous action
const logIn = ({ email, password }) => async (dispatch, state) => {
  dispatch(logInRequest());
  try {
    const user = await api.authenticateUser({ email, password });
    dispatch(logInSuccess({ user }));
  } catch (error) {
    dispatch(logInError({ error }));
  }
};

// Dispatch logIn like any other action
dispatch(logIn({ email, password }));

Note: dispatch will return the result of any async actions, opening up possibilities like chaining promises from dispatch:

dispatch(logIn({ email, password })).then(() => {
  // Do stuff...
});

API

useSimpleState

A custom React hook that lets us access our state and dispatch function from inside components.

useSimpleState(mapState?: Function, mapDispatch?: Function): Array<mixed>
Usage:
const [state, dispatch] = useSimpleState();

Returns an array containing a state object and a dispatch function.

useSimpleState has two optional parameters: mapState and mapDispatch:

mapState

If mapState is passed, it will be used to compute the output state and the result will be passed to the first element of the array returned by useSimpleState.

mapState(state: Object): Object
Usage
const mapState = state => ({ total: state.countA + state.countB });
const [computedState, dispatch] = useSimpleState(mapState);

Note: null can also be passed if you want to use mapDispatch but have no use for a mapState function.

mapDispatch

mapDispatch can be used to pre-wrap actions in dispatch. If mapDispatch is passed, the result will be given as the second element of the array returned by useSimpleState.

mapDispatch(dispatch: Function): *
Usage
const mapDispatch = dispatch => ({
  dispatchA: () => dispatch(actionA()),
  dispatchB: () => dispatch(actionB()),
  dispatchC: () => dispatch(actionC())
});
const [state, computedDispatch] = useSimpleState(null, mapDispatch);

computedDispatch.dispatchA();

SimpleStateProvider

A React component that wraps an app's root component and makes state available to our React app.

Usage
const Root = () => (
  <StateProvider state={initialState} reducers={[reducer]} middleware={[middleware]}>
    <App/>
  </StateProvider>
);

Has two mandatory props: initialState and reducers, as well as an optional prop: middleware

initialState

An object representing the initial state of our app.

reducers

An array of reducers.

Reducers take an action as well as the current state and use these to derive a new state. If a reducer returns undefined there will be no state update.

Reducers should have the following API:

(state, action) => nextState
middleware

An array of middleware functions.

Middleware functions are used to handle side effects in our app.

A middleware function is given two parameters: state and action.

If any middleware returns null, the triggering action will be blocked from reaching our reducers and the state will not be updated.

Usage
function myMiddleware (action, state) {
  if (action.type === 'ADD') {
    console.log(`${state.count} + ${action.payload} = ${state.count + action.payload}`);
  }
}

SimpleStateConsumer

A React component that is used to access the state context with a similar API to the useSimpleState hook.

Note: this component is a temporary workaround to be used until hooks are able to bail us out of the rendering process.

Usage
const Greeting = () => (
  <SimpleStateConsumer>
    {({ state, dispatch }) => (
      <>
        <h1>{state.greeting}</h1>
        <button onClick={() => dispatch(setGreeting('hello'))}> Change greeting </button>
      </>
    )}
  </SimpleStateConsumer>
);

Has two optional props: mapState and mapDispatch. Use of mapState is strongly encouraged so that each consumer only subscribes to specific changes in the state. If no mapState is passed, your consumer will re-render on every single state change.

The following props are identical to those of useSimpleState.

mapState

If mapState is passed, it will be used to compute the output state and the result will be passed to the state key of SimpleStateConsumer's render prop.

mapState(state: Object): Object
Usage
const mapState = state => ({ total: state.countA + state.countB });

const Total = () => (
  <SimpleStateConsumer mapState={mapState}>
    {({ state }) => (
      <span>Total: {state.total}</span>
    )}
  </SimpleStateConsumer>
);
mapDispatch

mapDispatch can be used to pre-wrap actions in dispatch. If mapDispatch is passed, the result will be passed to the dispatch property of SimpleStateConsumer's render prop.

mapDispatch(dispatch: Function): *
Usage
const mapDispatch = dispatch => ({
  dispatchA: () => dispatch(actionA()),
  dispatchB: () => dispatch(actionB()),
  dispatchC: () => dispatch(actionC())
});

const Dispatcher = () => (
  <SimpleStateConsumer mapDispatch={mapDispatch}>
    {({ dispatch }) => (
      <>
        <button onClick={dispatch.dispatchA}>Dispatch A</button>
        <button onClick={dispatch.dispatchB}>Dispatch B</button>
        <button onClick={dispatch.dispatchC}>Dispatch C</button>
      </>
    )}
  </SimpleStateConsumer>
);