Skip to content

StateAdapt syntax is minimal, speeding up experimentation and learning.

If you wanted to play with an idea for a counter, this is the code you'd write with a few state management libraries:

tsx
import { numberAdapter } from '@state-adapt/core/adapters';
import { useAdapt } from '@state-adapt/react';

const Component = () => {
  const [count, setCount] = useAdapt(0, numberAdapter);
  // ... setCount.add(1)
};
tsx
import { useState } from 'react';

const Component = () => {
  const [count, setCount] = useState(0);
  const increment = (qty: number) => setCount(c => c + qty);
  const decrement = (qty: number) => setCount(c => c - qty);
  // ...
};
tsx
import { create } from 'zustand';

type State = {
  count: number;
};

type Actions = {
  increment: (qty: number) => void;
  decrement: (qty: number) => void;
};

const useCountStore = create<State & Actions>(set => ({
  count: 0,
  increment: (qty: number) => set(state => ({ count: state.count + qty })),
  decrement: (qty: number) => set(state => ({ count: state.count - qty })),
}));

const Component = () => {
  const count = useCountStore(state => state.count);
  const increment = useCountStore(state => state.increment);
  const decrement = useCountStore(state => state.decrement);
  // ...
};
tsx
import { createSlice, configureStore } from '@reduxjs/toolkit';
import { useSelector, useDispatch } from 'react-redux';

const countSlice = createSlice({
  name: 'count',
  initialState: { count: 0 },
  reducers: {
    incremented: (state, qty: number) => {
      state.count += qty;
    },
    decremented: (state, qty: number) => {
      state.count -= qty;
    },
  },
});

const countStore = configureStore({ reducer: countSlice.reducer });

const Component = () => {
  const count = useSelector(state => state.count);
  const dispatch = useDispatch();
  const increment = (qty: number) => dispatch(countSlice.actions.incremented(qty));
  const decrement = (qty: number) => dispatch(countSlice.actions.decremented(qty));
  // ...
};
tsx
import { useAdapt } from '@state-adapt/react';

const Component = () => {
  const [count, setCount] = useAdapt(0);
  // ...
};

This isn't cheating because a key benefit of state adapters is that they are composable, which enables reuse of numberAdapter.

It's not cheating with the state shape either, because most state management libraries require containing objects, but there is no reason to use one until it's needed.

You can even add a second counter the same way and nobody will care:

tsx
import { numberAdapter } from '@state-adapt/core/adapters';
import { useAdapt } from '@state-adapt/react';

const Component = () => {
  const [count1, setCount1] = useAdapt(0, numberAdapter);
  const [count2, setCount2] = useAdapt(0, numberAdapter);
  // ...
};

But you could also create a parent state if you wanted to, and here is how they compare there:

tsx
import { numberAdapter } from '@state-adapt/core/adapters';
import { useAdapt } from '@state-adapt/react';

type State = {
  count1: number;
  count2: number;
};

const initialState: State = {
  count1: 0,
  count2: 0,
};

const adapter = joinAdapters<State>()({
  count1: numberAdapter,
  count2: numberAdapter,
})();

const Component = () => {
  const [counts, setCounts] = useAdapt(initialState, adapter);
  // ... setCounts.addCount1(1)
};
tsx
import { useState } from 'react';

const Component = () => {
  const [counts, setCounts] = useState({
    count1: 0,
    count2: 0,
  });

  const increment = (qty: number, id: number) => {
    const key = 'count' + id;
    setCounts(state => ({ ...state, [key]: state[key] + qty }));
  };
  const decrement = (qty: number, id: number) => {
    const key = 'count' + id;
    setCounts(state => ({ ...state, [key]: state[key] - qty }));
  };
  // ...
};
tsx
import { create } from 'zustand';

type State = {
  count1: number;
  count2: number;
};

type Actions = {
  increment: (qty: number, id: number) => void;
  decrement: (qty: number, id: number) => void;
};

const useCountStore = create<State & Actions>(set => ({
  count: 0,
  increment: (qty: number, id: number) =>
    set(state => {
      const key = 'count' + id;
      return { [key]: state[key] + qty };
    }),
  decrement: (qty: number, id: number) =>
    set(state => {
      const key = 'count' + id;
      return { [key]: state[key] - qty };
    }),
}));

const Component = () => {
  const count1 = useCountStore(state => state.count1);
  const count2 = useCountStore(state => state.count2);
  const increment = useCountStore(state => state.increment);
  const decrement = useCountStore(state => state.decrement);
  // ...
};
tsx
import { createSlice, configureStore } from '@reduxjs/toolkit';

const countSlice = createSlice({
  name: 'counts',
  initialState: {
    count1: 0,
    count2: 0,
  },
  reducers: {
    incremented: (state, action) => {
      const { qty, id } = action.payload;
      state['count' + id] += qty;
    },
    decremented: (state, action) => {
      const { qty, id } = action.payload;
      state['count' + id] -= qty;
    },
  },
});

const countStore = configureStore({ reducer: countSlice.reducer });
tsx
import { useAdapt } from '@state-adapt/react';

const Component = () => {
  const [counts, setCounts] = useAdapt({
    count1: 0,
    count2: 0,
  });
  // ...
};

A lot closer, but some repetition will be addressed in StateAdapt 4.