npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

t-state

v9.2.3

Published

Global state manager for Typescript projects

Downloads

408

Readme

T-State

A global state manager for React with Typescript in mind

Creating stores

Stores can be strongly typed by passing the format of the state and optionally the reducers payloads.

import Store from 't-state';

type TestState = {
  firstName: string;
  lastName: string;
};

const testStore = new Store<TestState>({
  debugName: 'test',
  state: {
    firstName: 'Hello',
    lastName: 'World',
  },
});

Using in components

Each store has hooks that can be used to derive/select state

useSelector

Allows for the selection or derivation of any value from a store and triggers re-renders whenever the selector value changes, eliminating unnecessary rerenders.

const Component = () => {
  const fullName = testStore.useSelector(
    (state) => `${state.firstName} ${state.lastName}`,
  );

  return <div>Name: {fullName}</div>;
};

By default, values are compared using shallow equality check, which compares values up to one level of depth. For more complex comparisons, you can use deepEqual or another custom equality function to avoid re-renders.

import { deepEqual } from 't-state';

const Component = () => {
  const fullName = testStore.useSelector(
    (state) =>
      [
        [
          {
            firstName: state.firstName,
          },
        ],
        [
          {
            lastName: state.lastName,
          },
        ],
      ] as const,
    { equalityFn: deepEqual },
  );

  return (
    <div>
      Name: {fullName[0][0].firstName} {fullName[1][0].lastName}
    </div>
  );
};

useKey

useKey is a hook that returns the value of a specific key.

const Component = () => {
  const firstName = testStore.useKey('firstName');

  return (
    <>
      <div>Name: {firstName}</div>

      <input
        onChange={(e) => testStore.setKey('firstName', e.currentTarget.value)}
      />
    </>
  );
};

Changing state

State changes can be made through the methods setKey, setState, and setPartialState, or by mutation using immer

Updating state via immer

With produceState, it is possible to change the state by mutating the values while maintaining the store's immutability. This is especially useful for updating "deep nested values". For more details and possibilities, consult the documentação do immer

testStore.produceState((draftState) => {
  draftState.firstName = 'John';
  draftState.lastName = 'Doe';
});

testStore.produceState((draftState) => {
  draftState.updating.aReally.deep.value = 'new value';
});

Debug via Redux Dev Tools

The Redux Dev Tools allows you to visualize all changes in each store.

Reacting to state changes

Outside of React, you can react to state changes with the subscribe method. It returns a function to unsubscribe from the subscription.

const unsubscribe = testStore.subscribe((prev, current) => {
  console.log('prev name', prev.firstName, prev.lastName);

  console.log('new name', current.firstName, current.lastName);
});

// unsubscribing
unsubscribe();

Using the observeChanges util, you can react more selectively to changes.

import { observeChanges } from 't-state';

testState.subscribe((prev, current) => {
  const observe = observeChanges(prev, current);

  observe
    .ifSelector((s) => `${s.firstName} ${s.lastName}`)
    .change.then((currentResult, prevResult) => {
      console.log('full name changed from', prevResult, 'to', currentResult);
    });
});

Creating stores inside components

Stores can also be created inside components using the useCreateStore hook allowing atomic updates optimization

import { useCreateStore } from 't-state/useCreateStore';

type TestState = {
  numOfClicks1: number;
  numOfClicks2: number;
};

const Component = () => {
  const testState = useCreateStore<TestState>({
    name: 'teste',
    state: {
      numOfClicks1: 0,
      numOfClicks2: 0,
    },
  });

  return (
    <>
      <Child store={testState} id="numOfClicks1" />
      <Child store={testState} id="numOfClicks1" />
    </>
  );
};

type ChildProps = {
  store: Store<TestState>;
  id: keyof TestState;
};

const Child = ({ store, id }: ChildProps) => {
  const [numOfClicks, setNumOfClicks] = store.useKey(id);

  return (
    <button type="button" onClick={() => setNumOfClicks(numOfClicks + 1)}>
      {id} num of clicks: {numOfClicks}
    </button>
  );
};

In the example above, each child component is only rendered when the part of the store it uses is changed, unlike what would happen if a simple useState was used.

Using middlewares

Middlewares can be used to intercept state change actions and block or modify the state.

const store = new Store({ state: { value: 0 } });

store.addMiddleware(({ current, next, action }) => {
  if (value < 0) {
    return false; // block state changes
  }

  if (value > 10) {
    return { value: 10 }; // return a new state to change the state
  }

  return true; // return true or `undefined` to do nothing
});

Create computed states

Computed states are states that are derived from other stores and are updated whenever the states they depend on change. The return of computed function is a store with some readonly methods like subscribe and useState

const store1 = new Store({ state: 2 });

const doubledValue = computed(store1, (state) => state * 2);

console.log(doubledValue.state); // 0

Use useComputed for creating computed states stores inside components

const Component = () => {
  const store1 = new Store({ state: 2 });

  const doubledValue = useComputed(store1, (state) => state * 2);

  const value = doubledValue.useState();

  return <div>{value}</div>;
};

Debounce state changes

State changes can be throttled using the debounceSideEffects option

const store = new Store({
  state: { value: 0 },
  debounceSideEffects: {
    wait: 1000,
    maxWait: 2000,
  },
});