react-vessel
TypeScript icon, indicating that this package has built-in type declarations

0.2.2 • Public • Published

React Vessel

npm Codecov CircleCI

A different approach for managing your state in react. Build your applications with composing dynamic reducers through JSX api.

🕹 CodeSandbox demos 🕹
Counter

Installation

Install react-vessel:

npm install react-vessel

or with yarn:

yarn add react-vessel

What you can do with it

You can write a simple counter like this

Here is the simplest thing you can do with a vessel.

import React from 'react';
import { Vessel, Reducer } from 'react-vessel';
 
function Counter({ name }) {
  const { dispatch } = useParentVessel();
  const count = useParentState(name, 0);
 
  return (
    <div>
      <Reducer model={name} action="increment" reducer={(state = 0) => state + 1} />
      <Reducer model={name} action="decrement" reducer={(state = 0) => state - 1} />
      <div>{count}</div>
      <button type="button" onClick={() => dispatch({ type: 'count/increment' })}>
        Increment
      </button>
      <button type="button" onClick={() => dispatch({ type: 'count/decrement' })}>
        Decrement
      </button>
    </div>
  );
}
 
function App() {
  return (
    <Vessel>
      <Counter name="counter1" />
      <Counter name="counter2" />
    </Vessel>
  );
}

Notice how you could easily reuse the component.

Above code will produce following state in your vessel:

{
  "counter1": 0,
  "counter2": 0
}

You can access this state anywhere in your application

function MyComponent() {
  const count = useParentState('counter1', 0);
  return <div>{count}</div>;
}

Or you can use a component with render props if you prefer that:

function MyComponent() {
  return <WithVessel select="counter1" render={(count, { state, dispatch }) => {
    return <div>{count}</div>
  }}>
}

You can add/remove reducers dynamically

function MyComponent() {
  const [incrementEnabled, setIncrementEnabled] = useState();
 
  return (
    <React.Fragment>
      {incrementEnabled && (
        <Reducer model="my-counter" action="increment" reducer={(state = 0) => state + 1} />
      )}
      <Reducer model="my-counter" action="decrement" reducer={(state = 0) => state - 1} />
      <button type="button" onClick={() => setIncrementEnabled(!incrementEnabled)}>
        Enable/Disable Increment
      </button>
    </React.Fragment>
  );
}

You can combine multiple reducers inside a Model

<Model name="my-counter">
  <Reducer action="increment" reducer={(state = 0) => state + 1} />
  <Reducer action="decrement" reducer={(state = 0) => state - 1} />
</Model>

You can build a simple FormInput

function FormInput({ name, render }) {
  const value = useVesselState(`${name}.value`);
  const { dispatch } = useVesselDispatch();
 
  function onChange(payload) {
    dispatch({ type: `${name}/change`, payload });
  }
 
  return (
    <React.Fragment>
      <Reducer
        model={name}
        action="change"
        reducer={(state, payload) => ({ ...state, value: payload })}
      />
      {render({ onChange, value })}
    </React.Fragment>
  );
}
 
function TextInput({ name }) {
  return (
    <FormInput
      name={name}
      render={({ onChange, value }) => (
        <input value={value} onChange={e => onChange(e.target.value)} />
      )}
    />
  );
}
 
function App() {
  return (
    <Vessel>
      <TextInput name="smart-input" />
 
      <WithVessel
        select="input.value"
        render={(value, { state, dispatch }) => {
          return <p>{value}</p>;
        }}
      />
    </Vessel>
  );
}

You can write effects that runs after an action

function ValidateUsername() {
  const { dispatch } = useParentVessel();
  return (
    <Reducer model="username" action="validate-suc" reducer={(state) => ({ ...state, error: null })} />
    <Reducer model="username" action="validate-err" reducer={(state, payload) => ({ ...state, error: payload })} />
    <Effect
      on="username/changed"
      run={async action => {
        try {
          await api.validateUsername(action.payload);
          dispatch({ type: 'username/validate-suc' });
        } catch (err) {
          dispatch({ type: 'username/validate-err', payload: err.message });
        }
      }}
    />
  );
}
 
function App() {
  return (
    <Vessel>
      <TextInput name="username" />
 
      <ValidateUsername />
    </Vessel>
  );
}

Readme

Keywords

none

Package Sidebar

Install

npm i react-vessel

Weekly Downloads

1

Version

0.2.2

License

MIT

Unpacked Size

198 kB

Total Files

24

Last publish

Collaborators

  • nepjua