• Stars
    star
    107
  • Rank 321,788 (Top 7 %)
  • Language
    JavaScript
  • Created almost 6 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Simple hooks-based state management for React

Outstated

Simple hooks-based state management for React

Build Status Coverage Status Minzip Size license

Like unstated but with hooks

Installation

npm install outstated

Example

import React, {useState} from 'react';
import ReactDOM from 'react-dom';
import {Provider, useStore} from 'outstated';

const store = () => {
  const [count, setCount] = useState(0);

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);
  const reset = () => setCount(0);

  return {count, increment, decrement, reset};
};

function Counter() {
  const {count, increment, decrement, reset} = useStore(store);

  return (
    <div>
      <button onClick={decrement}>-</button>
      <span>{count}</span>
      <button onClick={increment}>+</button>
      <button onClick={reset}>reset</button>
    </div>
  );
}

ReactDOM.render(
  <Provider stores={[store]}>
    <Counter />
  </Provider>,
  document.getElementById('root')
);

For more examples, see the example/ directory.

Guide

Unstated is awesome, but doesn't really use hooks.
Can we build something similar to unstated with hooks to make something even nicer?

Introducing Outstated

I really like unstated. I really like hooks. I wanted a simple hook-based app state management solution. This is why I've built Outstated.

Outstated is built on top of React hooks, context and patterns surrounding those elements.

It has three pieces:

Store

It's a place to store our state and some of the logic for updating it.

Store is a very simple React hook (which means you can re-use it, use other hooks within it, etc).

import {useState} from 'React';

const store = () => {
  const [state, setState] = useState({test: true});

  const update = val => setState(val);

  return {state, update};
};

Note that stores use useState hook from React for managing state. When you call setState it triggers components to re-render, so be careful not to mutate state directly or your components won't re-render.

useStore

Next we'll need a piece to introduce our state back into the tree so that:

  • When state changes, our components re-render.
  • We can depend on our store state.
  • We can call functions exposed by the store.

For this we have the useStore hook which allows us to get global store instances by using specific store constructor.

function Counter() {
  const {count, decrement, increment} = useStore(counterStore);

  return (
    <div>
      <span>{count}</span>
      <button onClick={decrement}>-</button>
      <button onClick={increment}>+</button>
    </div>
  );
}
<Provider>

The final piece that Outstated has is <Provider> component. It has two roles:

  1. It initializes global instances of given stores (this is required because React expects the number of hooks to be consistent across re-renders)
  2. It uses a set of contexts to pass initialized instances of given stores to all the components down the tree.
    Different context is used for each store. This allows to only trigger re-renders in the components that use the updated store. As a (minor) downside of this approach - nested contexts are created for each store you pass.
render(
  <Provider stores={[counterStore]}>
    <Counter />
  </Provider>
);

Testing

Whenever we consider the way that we write the state in our apps we should be thinking about testing.
We want to make sure that our state containers have a clean way to test them.

Because our containers are just hooks, we can construct them in tests and assert different things about them very easily.

import {renderHook, act} from 'react-hooks-testing-library';

test('counter', async () => {
  let count, increment, decrement;
  renderHook(() => ({count, increment, decrement} = counterStore()));

  expect(count).toBe(0);

  act(() => increment());
  expect(count).toBe(1);

  act(() => decrement());
  expect(count).toBe(0);
});

Related

More Repositories

1

graffiti

Minimalistic GraphQL framework
JavaScript
306
star
2

microwork

Microwork - simple creation of distributed scalable microservices in node.js with RabbitMQ
JavaScript
97
star
3

rxstate

Simple opinionated state management library based on RxJS
JavaScript
50
star
4

generator-powder

Powder.js Yeoman generator
JavaScript
41
star
5

node-docker-pkg-demo

Demo of using Docker multi-stage builds with Zeit pkg to build small node-based images
JavaScript
14
star
6

postal.observe

A postal.js plugin that provides a way to get a subscription as Rx.Observable
JavaScript
9
star
7

reddmix

Remix-based Reddit client
TypeScript
8
star
8

particula

Zero-config Express.js Framework
JavaScript
8
star
9

microcore

Core library for simple creation of pipelinening microservices in Node.js with RabbitMQ
JavaScript
7
star
10

Steamgifts-Chrome-Extensions

Steamgifts.com new giveaways notifier for Chrome
HTML
6
star
11

youtube-control

Simple extensions for viewing YouTube subscriptions as lists (Youtube Video Deck alternative)
JavaScript
6
star
12

feedly-colorful-list-view

Tiny userscript that colorizes Feedly news entries by source.
JavaScript
5
star
13

rethinkdb-pubsub

Implementation of message queueing on top of RethinkDB changefeeds as a library
JavaScript
5
star
14

superagent-rx

A plugin for superagent that allows getting results as Rx.Observable
JavaScript
4
star
15

shard

Shard community platform
JavaScript
4
star
16

libcodezen

Adobe AIR, flex, as3 classes and components library
ActionScript
4
star
17

gsmCharacteristics

gsmCharacteristics parsing and transforming project
JavaScript
3
star
18

yamalight

My Profile Readme
3
star
19

warframe-alert-tg-bot

Simple Telegram bot for warframe that will notify you about interesting items in alerts and invasions
JavaScript
3
star
20

HumblePlayer

HumblePlayer - music player for your HumbleBundle soundtracks
JavaScript
3
star
21

particula-core-fastify

Fastify.js core for Particula
JavaScript
3
star
22

actions-mongo-test

Basic test of importing dump into mongodb in github actions
2
star
23

gatsby-mdx-bug-demo

Simple demo for MDX graphql issue
CSS
2
star
24

particula-plugin-next

Particula plugin that adds Next.js integration
JavaScript
2
star
25

MobilePOIStudentProject

JavaScript
1
star
26

presentation-asyncjs

Presentation on working with asynchronous javascript (promises, async/await, functional programming, reactive programming)
CSS
1
star
27

treeofsavior-translatehelper

Tree of Savior - Translation Helper app
JavaScript
1
star
28

gaming-knowledge-graph

Building a gaming knowledge graph
JavaScript
1
star