/react-twine

React state management library. As if we need another.

Primary LanguageTypeScriptMIT LicenseMIT

react-twine

Creating state

const counterState = createState(0);

function Counter() {
  const [count, setCounter] = useSharedState(counterState);
  const increment = () => setCounter(count + 1);

  return <button onClick={increment}>{count}</button>;
}

Creating actions

const counterState = createState(0);
const incrementAction = createAction(({value, set}) =>
  set(counterState, count => count + value)
);

function Counter() {
  const count = useValue(counterState);
  const increment = useAction(incrementAction, 1);

  return <button onClick={increment}>{count}</button>;
}

Creating selectors

const counterState = createState(0);
const counterDoubleSelector = createSelector(({get}) => get(counterState) * 2);

function Counter() {
  const [count, setCounter] = useSharedState(counterState);
  const countDouble = useValue(counterDoubleSelector);
  const increment = () => setCounter(count + 1);

  return (
    <p>
      <button onClick={increment}>{count}</button> * 2 = {countDouble}
    </p>
  );
}

Creating proxy state

const counterState = createState(0);
const counterDoubleState = createProxyState(
  ({get}) => get(counterState) * 2,
  ({value, set}) => set(counterState, value / 2)
);

function Counter() {
  const [count, setCounter] = useSharedState(counterState);
  const [countDouble, setCounterDouble] = useSharedState(counterDoubleState);
  const increment = () => setCounter(count + 1);
  const incrementDouble = () => setCounterDouble(countDouble + 1);

  return (
    <p>
      <button onClick={increment}>{count}</button> * 2 =
      <button onClick={incrementDouble}>{countDouble}</button>
    </p>
  );
}

Creating state factories

const itemStates = createStateFactory(key => `Item ${key}`);

function Item(props) {
  const item = useValue(itemStates(props.itemKey));

  return <p>This item: {item}</p>;
}

Initializing state asynchronously

const counterState = createState(async () => {
  await delay(1000);
  return 0;
});

function Counter() {
  const [count, setCounter] = useAsyncState(counterState);
  const increment = () => setCounter(count => count + 1);

  return <button onClick={increment}>{count ?? 'loading...'}</button>;
}

Selecting asynchronously

const counterState = createState(0);
const counterDoubleSelector = createSelector(async ({get}) => {
  const counter = get(counterState);
  await delay(1000);
  return counter * 2;
});

function Counter() {
  const [count, setCounter] = useSharedState(counterState);
  const [countDouble] = useAsyncValue(counterDoubleSelector);
  const increment = () => setCounter(count + 1);

  return (
    <p>
      <button onClick={increment}>{count}</button> * 2 =
      {countDouble ?? 'loading...'}
    </p>
  );
}