Incremental Complexity 
useState without regrets 
Clean state management should be easy, like useState.
Developers should feel completely free to use useState for simple features.
A smooth path to reducers 
But when state needs to change in more complex ways, there are 2 approaches:
Event handlers—scattered state logic ❌
Reducers—colocated state logic ✅
Reducers are great, but refactoring from useState to useReducer takes a lot of work.
StateAdapt provides a smoother path to reducers:
1. Replace useState with useAdapt 
function SimpleStateAdapt() {
  const [name, setName] = useState('Bob'); 
  const [name, setName] = useAdapt('Bob'); 
  return (
    <>
      <h2>Hello {name}!</h2>
      <h2>Hello {name.state}!</h2>
      <button onClick={() => setName('Bilbo')}>Change Name</button>
    </>
  );
}Result:
function SimpleStateAdapt() {
  const [name, setName] = useAdapt('Bob');
  return (
    <>
      <h2>Hello {name.state}!</h2>
      <button onClick={() => setName('Bilbo')}>Change Name</button>
    </>
  );
}2. Add Reducers 
function ReducedState() {
  const [name, setName] = useAdapt('Bob'); 
  const [name, setName] = useAdapt('Bob', {
    reverse: name => name.split('').reverse().join(''), // name type inferred
  }); 
  return (
    <>
      <h2>Hello {name.state}!</h2>
      <button onClick={() => setName('Bilbo')}>Change Name</button>
      <button onClick={() => setName.reverse()}>Reverse Name</button>
    </>
  );
}Result:
function ReducedState() {
  const [name, setName] = useAdapt('Bob', {
    reverse: name => name.split('').reverse().join(''),
  });
  return (
    <>
      <h2>Hello {name.state}!</h2>
      <button onClick={() => setName('Bilbo')}>Change Name</button>
      <button onClick={() => setName.reverse()}>Reverse Name</button>
    </>
  );
}A smooth path to shared state 
Moving local state to shared state should be easy.
useAdapt easily splits into adapt and useStore:
const nameStore = adapt('Bob', {
  reverse: name => name.split('').reverse().join(''), 
}); 
function SharedState() {
  const [name, setName] = useAdapt('Bob', {
    reverse: name => name.split('').reverse().join(''), 
  }); 
  const [name, setName] = useStore(nameStore); 
  return (
    <>
      <h2>Hello {name.state}!</h2>
      <button onClick={() => setName('Bilbo')}>Change Name</button>
      <button onClick={() => setName.reverse()}>Reverse Name</button>
    </>
  );
}Result:
const nameStore = adapt('Bob', {
  reverse: name => name.split('').reverse().join(''),
});
function SharedState() {
  const [name, setName] = useStore(nameStore);
  return (
    <>
      <h2>Hello {name.state}!</h2>
      <button onClick={() => setName('Bilbo')}>Change Name</button>
      <button onClick={() => setName.reverse()}>Reverse Name</button>
    </>
  );
}A smooth path to shared, derived state 
Nothing is as easy as derived state in React components:
const [name, setName] = useAdapt(nameStore);
const randomCaseName = name.state
  .split('') 
  .map(c => (Math.random() > 0.5 ? c : c.toUpperCase())) 
  .join(''); 
// ...Since nothing can match this syntax, anything you do to share this logic with other components will require some refactoring.
One way StateAdapt addresses this is by allowing selectors to be defined alongside state from the start:
function SharedDerivedState() {
  const [name, setName] = useAdapt('Bob', {
    reverse: name => name.split('').reverse().join(''),
    seletors: {
      randomCase: (
        name, 
      ) =>
        name
          .split('') 
          .map(c => (Math.random() > 0.5 ? c : c.toUpperCase())) 
          .join(''), 
    }, 
  });
  return (
    <>
      <h2>Hello {name.state}!</h2>
      <h2>Hello {name.randomCase}!</h2>
      <button onClick={() => setName('Bilbo')}>Change Name</button>
      <button onClick={() => setName.reverse()}>Reverse Name</button>
    </>
  );
}Now if you need to share it, the selectors can just move with the state:
const nameStore = adapt('Bob', {
  reverse: name => name.split('').reverse().join(''), 
  seletors: {
    randomCase: (
      name, 
    ) =>
      name
        .split('') 
        .map(c => (Math.random() > 0.5 ? c : c.toUpperCase())) 
        .join(''), 
  }, 
}); 
function SharedDerivedState() {
  const [name, setName] = useAdapt('Bob', {
    reverse: name => name.split('').reverse().join(''), 
    seletors: {
      randomCase: (
        name, 
      ) =>
        name
          .split('') 
          .map(c => (Math.random() > 0.5 ? c : c.toUpperCase())) 
          .join(''), 
    }, 
  }); 
  const [name, setName] = useStore(nameStore); 
  return (
    <>
      <h2>Hello {name.state}!</h2>
      <h2>Hello {name.randomCase}!</h2>
      <button onClick={() => setName('Bilbo')}>Change Name</button>
      <button onClick={() => setName.reverse()}>Reverse Name</button>
    </>
  );
}Result:
const nameStore = adapt('Bob', {
  reverse: name => name.split('').reverse().join(''),
  seletors: {
    randomCase: name =>
      name
        .split('')
        .map(c => (Math.random() > 0.5 ? c : c.toUpperCase()))
        .join(''),
  },
});
function SharedDerivedState() {
  const [name, setName] = useStore(nameStore);
  return (
    <>
      <h2>Hello {name.state}!</h2>
      <h2>Hello {name.randomCase}!</h2>
      <button onClick={() => setName('Bilbo')}>Change Name</button>
      <button onClick={() => setName.reverse()}>Reverse Name</button>
    </>
  );
}A smooth path to state logic reuse 
Decoupled 
State logic that references specific event sources and specific state can require major refactoring if multiple states end up needing it.
State adapters provide a smooth path to extracting logic away from specific event sources and state:
const nameStore = adapt('Bob', { 
const nameAdapter = createAdapter<string>()({ 
  reverse: name => name.split('').reverse().join(''),
  selectors: {
    randomCase: name =>
      name
        .split('')
        .map(c => (Math.random() > 0.5 ? c : c.toUpperCase()))
        .join(''),
  },
});
const name1Store = adapt('Bob', nameAdapter); 
const name2Store = adapt('Kat', nameAdapter); 
// ...Result:
const nameAdapter = createAdapter<string>()({
  reverse: name => name.split('').reverse().join(''),
  selectors: {
    randomCase: name =>
      name
        .split('')
        .map(c => (Math.random() > 0.5 ? c : c.toUpperCase()))
        .join(''),
  },
});
const name1Store = adapt('Bob', nameAdapter);
const name2Store = adapt('Kat', nameAdapter);
function StateAdapters() {
  const [name1, setName1] = useStore(name1Store);
  const [name2, setName2] = useStore(name2Store);
  return (
    <>
      <h2>Hello {name1.state}!</h2>
      <h2>Hello {name1.randomCase}!</h2>
      <button onClick={() => setName1('Bilbo')}>Change Name</button>
      <button onClick={() => setName1.reverse()}>Reverse Name</button>
      <h2>Hello {name2.state}!</h2>
      <h2>Hello {name2.randomCase}!</h2>
      <button onClick={() => setName2('Bilbo')}>Change Name</button>
      <button onClick={() => setName2.reverse()}>Reverse Name</button>
    </>
  );
}Composable 
State adapters provide a smooth path to adapting to changes in state shape. If you start with a simple boolean adapter, for example:
const booleanAdapter = createAdapter<boolean>()({
  toggle: state => !state,
});And later decide to have multiple boolean properties of a larger state object:
type State = {
  isActive: boolean;
  isVisible: boolean;
};You can reuse the simple boolean logic by creating a joined adapter that extends it:
const adapter = joinAdapters<State>()({
  isActive: booleanAdapter,
  isVisible: booleanAdapter,
})();This creates reducers in adapter called toggleIsActive and toggleIsVisible that toggle the respective properties.
State adapters are also an opportunity to share generic state management logic. Check out the adapters you can import from @state-adapt/core/adapters.
A smooth path to reactive state 
When multiple states need to change after an event, there are 2 approaches:
Event handlers updating multiple states—scattered state changes ❌
States reacting to events—colocated state changes ✅
Reactive state is great, but it takes a lot of work to refactor to a state management library that supports event-driven state.
StateAdapt provides a smooth path to reactive state:
// ...
const onResetAll = source(); // Event source
const name1Store = adapt('Bob', nameAdapter); 
const name1Store = adapt('Bob', { 
  adapter: nameAdapter, 
  sources: { reset: onResetAll }, // calls `reset` reducer (included)
}); 
const name2Store = adapt('Kat', nameAdapter); 
const name2Store = adapt('Kat', { 
  adapter: nameAdapter, 
  sources: { reset: onResetAll }, // calls `reset` reducer (included)
}); 
// ...
      <button onClick={onResetAll}>Reset All</button>
    </>
  );
}Result:
const nameAdapter = createAdapter<string>()({
  reverse: name => name.split('').reverse().join(''),
  selectors: {
    randomCase: name =>
      name
        .split('')
        .map(c => (Math.random() > 0.5 ? c : c.toUpperCase()))
        .join(''),
  },
});
const onResetAll = source();
const name1Store = adapt('Bob', {
  adapter: nameAdapter,
  sources: { reset: onResetAll },
});
const name2Store = adapt('Kat', {
  adapter: nameAdapter,
  sources: { reset: onResetAll },
});
function ReactiveState() {
  const [name1, setName1] = useStore(name1Store);
  const [name2, setName2] = useStore(name2Store);
  return (
    <>
      <h2>Hello {name1.state}!</h2>
      <h2>Hello {name1.randomCase}!</h2>
      <button onClick={() => setName1('Bilbo')}>Change Name</button>
      <button onClick={() => setName1.reverse()}>Reverse Name</button>
      <h2>Hello {name2.state}!</h2>
      <h2>Hello {name2.randomCase}!</h2>
      <button onClick={() => setName2('Bilbo')}>Change Name</button>
      <button onClick={() => setName2.reverse()}>Reverse Name</button>
      <button onClick={onResetAll}>Reset All</button>
    </>
  );
}A smooth path to multi-store, shared, derived states 
State derived from multiple stores can glitch and over-compute in some libraries, especially if RxJS-based.
But StateAdapt's joinStores is glitch-free and efficient, preventing the need for complicated workarounds and refactors:
// ...
const name12Store = joinStores({ 
  name1: name1Store, 
  name2: name2Store, 
})({ 
  bobcat: s => s.name1 === 'Bob' && s.name2 === 'Kat'
})(); 
// ...
  const [{ bobcat }] = useStore(name12Store); 
  // ...
      {bobcat && <h2>Hello, bobcat!</h2>} 
    </>
  );
}Result:
const nameAdapter = createAdapter<string>()({
  reverse: name => name.split('').reverse().join(''),
  selectors: {
    randomCase: name =>
      name
        .split('')
        .map(c => (Math.random() > 0.5 ? c : c.toUpperCase()))
        .join(''),
  },
});
const onResetAll = source();
const name1Store = adapt('Bob', {
  adapter: nameAdapter,
  sources: { reset: onResetAll },
});
const name2Store = adapt('Kat', {
  adapter: nameAdapter,
  sources: { reset: onResetAll },
});
const name12Store = joinStores({
  name1: name1Store,
  name2: name2Store,
})({
  bobcat: s => s.name1 === 'Bob' && s.name2 === 'Kat',
})();
function MultiStoreSharedDerivedState() {
  const [name1, setName1] = useStore(name1Store);
  const [name2, setName2] = useStore(name2Store);
  const [{ bobcat }] = useStore(name12Store);
  return (
    <>
      <h2>Hello {name1.state}!</h2>
      <h2>Hello {name1.randomCase}!</h2>
      <button onClick={() => setName1('Bilbo')}>Change Name</button>
      <button onClick={() => setName1.reverse()}>Reverse Name</button>
      <h2>Hello {name2.state}!</h2>
      <h2>Hello {name2.randomCase}!</h2>
      <button onClick={() => setName2('Bilbo')}>Change Name</button>
      <button onClick={() => setName2.reverse()}>Reverse Name</button>
      <button onClick={onResetAll}>Reset All</button>
      {bobcat && <h2>Hello, bobcat!</h2>}
    </>
  );
}A smooth path to derived events 
RxJS is the only way to smoothly scale to complex event-driven features.
StateAdapt sources extend RxJS observables, and StateAdapt stores directly reference RxJS observables and react to them:
// ...
const name1Store = adapt('Bob', { 
const name1Store = adapt('Loading...', { 
  adapter: nameAdapter,
  sources: { reset: onResetAll }, 
  sources: { 
    set: of('Bob').pipe(delay(3000)), // Any observable
    reset: onResetAll, 
  }, 
});
const name2Store = adapt('Kat', { 
const name2Store = adapt('Loading...', { 
  adapter: nameAdapter,
  sources: { reset: onResetAll }, 
  sources: { 
    set: of('Kat').pipe(delay(3000)), // Any observable
    reset: onResetAll, 
  }, 
});
// ...Result:
const nameAdapter = createAdapter<string>()({
  reverse: name => name.split('').reverse().join(''),
  selectors: {
    randomCase: name =>
      name
        .split('')
        .map(c => (Math.random() > 0.5 ? c : c.toUpperCase()))
        .join(''),
  },
});
const onResetAll = source();
const name1Store = adapt('Loading...', {
  adapter: nameAdapter,
  sources: {
    set: of('Bob').pipe(delay(3000)),
    reset: onResetAll,
  },
});
const name2Store = adapt('Loading...', {
  adapter: nameAdapter,
  sources: {
    set: of('Kat').pipe(delay(3000)),
    reset: onResetAll,
  },
});
const name12Store = joinStores({
  name1: name1Store,
  name2: name2Store,
})({
  bobcat: s => s.name1 === 'Bob' && s.name2 === 'Kat',
})();
function DerivedEvents() {
  const [name1, setName1] = useStore(name1Store);
  const [name2, setName2] = useStore(name2Store);
  const [{ bobcat }] = useStore(name12Store);
  return (
    <>
      <h2>Hello {name1.state}!</h2>
      <h2>Hello {name1.randomCase}!</h2>
      <button onClick={() => setName1('Bilbo')}>Change Name</button>
      <button onClick={() => setName1.reverse()}>Reverse Name</button>
      <h2>Hello {name2.state}!</h2>
      <h2>Hello {name2.randomCase}!</h2>
      <button onClick={() => setName2('Bilbo')}>Change Name</button>
      <button onClick={() => setName2.reverse()}>Reverse Name</button>
      <button onClick={onResetAll}>Reset All</button>
      {bobcat && <h2>Hello, bobcat!</h2>}
    </>
  );
}Automatic State Lifecycle 
For state to be fully reactive, it cannot rely on external control code, including initialization and cleanup code.
StateAdapt stores know when they are being used, and automatically initialize and cleanup their state.
So, for this store:
const name1Store = adapt('Loading...', {
  sources: of('Bob').pipe(delay(3000)),
});When this component mounts:
function AutomaticStateLifecycle() {
  const [name1, setName1] = useStore(name1Store);
  // ...Only then will the state be initialized with Loading... and the observable created by of('Bob').pipe(delay(3000)) receive a subscription. After 3 seconds, the name will change to 'Bob'.
Then, when AutomaticStateLifecycle unmounts, as long as no other components are using name1Store, the state will be cleared.
Then, when AutomaticStateLifecycle mounts again, the state will be re-initialized to 'Loading...', the observable created with of('Bob').pipe(delay(3000)) will be subscribed to again, and after 3 seconds the name will change to 'Bob' again.
In situations where you want to keep the store permanently active, you can manually subscribe to its state$:
const name1Store = adapt('Loading...', {
  sources: of('Bob').pipe(delay(3000)),
});
name1Store.state$.subscribe();