
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
A global state manager for React with Typescript in mind
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',
},
});
Each store has hooks that can be used to derive/select state
useSelectorAllows 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: React.FC = () => {
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>
);
};
useKeyuseKey is a hook that returns the value of a specific key.
const Component: React.FC = () => {
const firstName = testStore.useKey('firstName');
return (
<>
<div>Name: {firstName}</div>
<input
onChange={(e) => testStore.setKey('firstName', e.currentTarget.value)}
/>
</>
);
};
State changes can be made through the methods setKey, setState, and setPartialState, or by mutation using 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 immer documentation
testStore.produceState((draftState) => {
draftState.firstName = 'John';
draftState.lastName = 'Doe';
});
testStore.produceState((draftState) => {
draftState.updating.aReally.deep.value = 'new value';
});
The Redux Dev Tools allows you to visualize all changes in each store.
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';
testStore.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);
});
});
Stores can also be created inside components using the useCreateStore hook allowing atomic updates optimization
import { useCreateStore } from 't-state/hooks';
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.
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 (next.value < 0) {
return false; // block state changes
}
if (next.value > 10) {
return { value: 10 }; // return a new state to change the state
}
return true; // return true or `undefined` to do nothing
});
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); // 4
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>;
};
Stores can be initialized lazily using functions for better performance:
const store = new Store({
state: () => ({
expensiveData: computeExpensiveData(),
timestamp: Date.now(),
}),
});
Multiple state updates can be batched together to prevent unnecessary renders:
store.batch(() => {
store.setKey('firstName', 'John');
store.setKey('lastName', 'Doe');
store.setKey('age', 30);
});
// Only one re-render occurs after all updates
T-State provides built-in equality functions to optimize re-renders:
import { shallowEqual, deepEqual } from 't-state';
// Use shallow equality (default)
const name = store.useSelector(
(state) => ({ first: state.firstName, last: state.lastName }),
{ equalityFn: shallowEqual },
);
// Use deep equality for complex objects
const complexData = store.useSelector((state) => state.nestedObject, {
equalityFn: deepEqual,
});
State changes can be throttled using the debounceSideEffects option
const store = new Store({
state: { value: 0 },
debounceSideEffects: {
wait: 1000,
maxWait: 2000,
},
});
type StoreProps<T> = {
state: T | (() => T);
debugName?: string;
debounceSideEffects?: {
wait: number;
maxWait?: number;
};
};
setKey<K extends keyof T>(key: K, value: T[K]): void - Set a specific key in the statesetState(state: T): void - Replace the entire statesetPartialState(partialState: Partial<T>): void - Update multiple keys at onceproduceState(producer: (draft: T) => void): void - Update state using Immer draftbatch(fn: () => void): void - Batch multiple state updatessubscribe(callback: (prev: T, current: T) => void): () => void - Subscribe to state changesaddMiddleware(middleware: MiddlewareFn<T>): void - Add middleware for state changesuseSelector<S>(selector: (state: T) => S, options?: SelectorOptions): S - Select derived stateuseKey<K extends keyof T>(key: K): T[K] - Get value of specific keyuseState(): T - Get entire statet-state/hooksuseCreateStore<T>(props: StoreProps<T>): Store<T> - Create store within componentuseStoreSnapshot<T, S>(store: Store<T>, selector: (state: T) => S, when: (state: T) => boolean): S - Conditional state snapshotsuseSelectFromStore<T, S>(store: Store<T>, selector: (state: T) => S, options?: SelectorOptions): S - External store selectiont-state/computedcomputed<T, S>(store: Store<T>, selector: (state: T) => S): ComputedStore<S> - Create computed stateuseComputed<T, S>(store: Store<T>, selector: (state: T) => S): ComputedStore<S> - Create computed state in componentt-stateshallowEqual(a: any, b: any): boolean - Shallow equality comparisondeepEqual(a: any, b: any): boolean - Deep equality comparisonobserveChanges(prev: T, current: T): ChangeObserver<T> - Observe specific changestype SelectorOptions = {
equalityFn?: EqualityFn | false;
};
type EqualityFn = (a: any, b: any) => boolean;
type MiddlewareFn<T> = {
(args: { current: T; next: T; action: string }): boolean | T | void;
};
src/
├── main.ts # Core Store class and main functionality
├── hooks.tsx # Additional React hooks
├── computed.ts # Computed states functionality
├── subscribeUtils.ts # Subscription utilities and observeChanges
├── deepEqual.ts # Deep equality comparison utility
├── shallowEqual.ts # Shallow equality comparison utility
├── useSyncExternalStoreWithSelector.ts # Custom external store hook implementation
├── devTools.ts # Redux DevTools integration
└── utils.ts # General utility functions
test/ # Test files
├── setup.ts # Test environment setup
├── *.test.ts # Individual test files
└── *.test.tsx # React component tests
main.ts - Contains the core Store class with all state management functionalityhooks.tsx - Additional React hooks like useCreateStore, useStoreSnapshot, useSelectFromStorecomputed.ts - Computed state functionality that automatically updates when dependencies changesubscribeUtils.ts - Contains observeChanges utility for selective change observationdeepEqual.ts and shallowEqual.ts provide optimized comparison functionsFAQs
Global state manager for Typescript projects
The npm package t-state receives a total of 8 weekly downloads. As such, t-state popularity was classified as not popular.
We found that t-state demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.