Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

zustand

Package Overview
Dependencies
Maintainers
1
Versions
142
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

zustand - npm Package Compare versions

Comparing version 0.1.3 to 0.2.0

32

dist/cjs/index.d.ts

@@ -1,13 +0,21 @@

declare type StateListener<T> = (state: T) => void;
declare type StateSelector<T, U> = (state: T) => U;
declare type PartialState<T> = Partial<T> | ((state: T) => Partial<T>);
export default function create<State extends Record<string, any>, SetState extends (partialState: PartialState<Record<string, any>>) => void, GetState extends () => Record<string, any>>(createState: (set: SetState, get: GetState) => State): [{
(): State;
<U>(selector: StateSelector<State, U>, dependencies?: readonly any[] | undefined): U;
}, {
export declare type State = Record<string, any>;
export declare type StateListener<T extends State, U = T> = (state: U) => void;
export declare type StateSelector<T extends State, U> = (state: T) => U;
export declare type PartialState<T extends State> = Partial<T> | ((state: T) => Partial<T>);
export declare type SetState<T extends State> = (partial: PartialState<T>) => void;
export declare type GetState<T extends State> = () => T;
export interface Subscribe<T> {
(listener: StateListener<T>): () => void;
<U>(selector: StateSelector<T, U>, listener: StateListener<T, U>): () => void;
}
export interface UseStore<T> {
(): T;
<U>(selector: StateSelector<T, U>, dependencies?: ReadonlyArray<any>): U;
}
export interface StoreApi<T> {
getState: GetState<T>;
setState: SetState<T>;
subscribe: Subscribe<T>;
destroy: () => void;
getState: () => State;
setState: (partialState: PartialState<State>) => void;
subscribe: (listener: StateListener<State>) => () => void;
}];
export {};
}
export default function create<TState extends State>(createState: (set: SetState<State>, get: GetState<State>) => TState): [UseStore<TState>, StoreApi<TState>];

@@ -36,7 +36,11 @@ 'use strict';

var setState = function setState(partialState) {
state = Object.assign({}, state, typeof partialState === 'function' ? partialState(state) : partialState);
listeners.forEach(function (listener) {
return listener(state);
});
var setState = function setState(partial) {
var partialState = typeof partial === 'function' ? partial(state) : partial;
if (partialState !== state) {
state = Object.assign({}, state, partialState);
listeners.forEach(function (listener) {
return listener(state);
});
}
};

@@ -46,8 +50,23 @@

return state;
};
}; // Optional selector param goes first so we can infer its return type and use
// it for listener
var subscribe = function subscribe(listener) {
var subscribe = function subscribe(selectorOrListener, listenerOrUndef) {
var listener = selectorOrListener; // Existance of second param means a selector was passed in
if (listenerOrUndef) {
// We know selector is not type StateListener so it must be StateSelector
var selector = selectorOrListener;
var stateSlice = selector(state);
listener = function listener() {
var selectedSlice = selector(state);
if (!shallowEqual(stateSlice, stateSlice = selectedSlice)) listenerOrUndef(stateSlice);
};
}
listeners.add(listener);
return function () {
listeners["delete"](listener);
return void listeners["delete"](listener);
};

@@ -58,38 +77,36 @@ };

listeners.clear();
state = {};
};
function useStore(selector, dependencies) {
// State selector gets entire state if no selector was passed in
var stateSelector = typeof selector === 'function' ? selector : getState;
var selectState = react.useCallback(stateSelector, dependencies);
var selectStateRef = react.useRef(selectState);
var useStore = function useStore(selector, dependencies) {
var selectorRef = react.useRef(selector);
var depsRef = react.useRef(dependencies);
var _useReducer = react.useReducer(reducer, state, selectState),
var _useReducer = react.useReducer(reducer, state, // Optional third argument but required to not be 'undefined'
selector),
stateSlice = _useReducer[0],
dispatch = _useReducer[1]; // Call new selector if it has changed
dispatch = _useReducer[1]; // Need to manually get state slice if selector has changed with no deps or
// deps exist and have changed
if (selectState !== selectStateRef.current) stateSlice = selectState(state); // Store in ref to enable updating without rerunning subscribe/unsubscribe
if (selector && (!dependencies && selector !== selectorRef.current || dependencies && !shallowEqual(dependencies, depsRef.current))) {
stateSlice = selector(state);
} // Update refs synchronously after view has been updated
var stateSliceRef = react.useRef(stateSlice); // Update refs only after view has been updated
react.useLayoutEffect(function () {
selectStateRef.current = selectState;
stateSliceRef.current = stateSlice;
}, [selectState, stateSlice]); // Subscribe/unsubscribe to the store only on mount/unmount
selectorRef.current = selector;
depsRef.current = dependencies;
}, dependencies || [selector]);
react.useLayoutEffect(function () {
return subscribe(function () {
// Use the last selector passed to useStore to get current state slice
var selectedSlice = selectStateRef.current(state); // Shallow compare previous state slice with current and rerender only if changed
if (!shallowEqual(stateSliceRef.current, selectedSlice)) dispatch(selectedSlice);
});
}, []);
return selector ? subscribe( // Truthy check because it might be possible to set selectorRef to
function () {
return selectorRef.current ? selectorRef.current(state) : state;
}, dispatch) : subscribe(dispatch); // Only resubscribe to the store when changing selector from function to
// undefined or undefined to function
}, [!selector]);
return stateSlice;
}
};
var state = createState(setState, getState);
var api = {
return [useStore, {
destroy: destroy,

@@ -99,6 +116,5 @@ getState: getState,

subscribe: subscribe
};
return [useStore, api];
}];
}
module.exports = create;

@@ -1,13 +0,21 @@

declare type StateListener<T> = (state: T) => void;
declare type StateSelector<T, U> = (state: T) => U;
declare type PartialState<T> = Partial<T> | ((state: T) => Partial<T>);
export default function create<State extends Record<string, any>, SetState extends (partialState: PartialState<Record<string, any>>) => void, GetState extends () => Record<string, any>>(createState: (set: SetState, get: GetState) => State): [{
(): State;
<U>(selector: StateSelector<State, U>, dependencies?: readonly any[] | undefined): U;
}, {
export declare type State = Record<string, any>;
export declare type StateListener<T extends State, U = T> = (state: U) => void;
export declare type StateSelector<T extends State, U> = (state: T) => U;
export declare type PartialState<T extends State> = Partial<T> | ((state: T) => Partial<T>);
export declare type SetState<T extends State> = (partial: PartialState<T>) => void;
export declare type GetState<T extends State> = () => T;
export interface Subscribe<T> {
(listener: StateListener<T>): () => void;
<U>(selector: StateSelector<T, U>, listener: StateListener<T, U>): () => void;
}
export interface UseStore<T> {
(): T;
<U>(selector: StateSelector<T, U>, dependencies?: ReadonlyArray<any>): U;
}
export interface StoreApi<T> {
getState: GetState<T>;
setState: SetState<T>;
subscribe: Subscribe<T>;
destroy: () => void;
getState: () => State;
setState: (partialState: PartialState<State>) => void;
subscribe: (listener: StateListener<State>) => () => void;
}];
export {};
}
export default function create<TState extends State>(createState: (set: SetState<State>, get: GetState<State>) => TState): [UseStore<TState>, StoreApi<TState>];

@@ -1,2 +0,2 @@

import { useCallback, useRef, useReducer, useLayoutEffect } from 'react';
import { useRef, useReducer, useLayoutEffect } from 'react';

@@ -32,14 +32,31 @@ function shallowEqual(objA, objB) {

const setState = partialState => {
state = Object.assign({}, state, typeof partialState === 'function' ? partialState(state) : partialState);
listeners.forEach(listener => listener(state));
const setState = partial => {
const partialState = typeof partial === 'function' ? partial(state) : partial;
if (partialState !== state) {
state = Object.assign({}, state, partialState);
listeners.forEach(listener => listener(state));
}
};
const getState = () => state;
const getState = () => state; // Optional selector param goes first so we can infer its return type and use
// it for listener
const subscribe = listener => {
const subscribe = (selectorOrListener, listenerOrUndef) => {
let listener = selectorOrListener; // Existance of second param means a selector was passed in
if (listenerOrUndef) {
// We know selector is not type StateListener so it must be StateSelector
const selector = selectorOrListener;
let stateSlice = selector(state);
listener = () => {
const selectedSlice = selector(state);
if (!shallowEqual(stateSlice, stateSlice = selectedSlice)) listenerOrUndef(stateSlice);
};
}
listeners.add(listener);
return () => {
listeners.delete(listener);
};
return () => void listeners.delete(listener);
};

@@ -49,34 +66,30 @@

listeners.clear();
state = {};
};
function useStore(selector, dependencies) {
// State selector gets entire state if no selector was passed in
const stateSelector = typeof selector === 'function' ? selector : getState;
const selectState = useCallback(stateSelector, dependencies);
const selectStateRef = useRef(selectState);
let [stateSlice, dispatch] = useReducer(reducer, state, selectState); // Call new selector if it has changed
const useStore = (selector, dependencies) => {
const selectorRef = useRef(selector);
const depsRef = useRef(dependencies);
let [stateSlice, dispatch] = useReducer(reducer, state, // Optional third argument but required to not be 'undefined'
selector); // Need to manually get state slice if selector has changed with no deps or
// deps exist and have changed
if (selectState !== selectStateRef.current) stateSlice = selectState(state); // Store in ref to enable updating without rerunning subscribe/unsubscribe
if (selector && (!dependencies && selector !== selectorRef.current || dependencies && !shallowEqual(dependencies, depsRef.current))) {
stateSlice = selector(state);
} // Update refs synchronously after view has been updated
const stateSliceRef = useRef(stateSlice); // Update refs only after view has been updated
useLayoutEffect(() => {
selectStateRef.current = selectState;
stateSliceRef.current = stateSlice;
}, [selectState, stateSlice]); // Subscribe/unsubscribe to the store only on mount/unmount
selectorRef.current = selector;
depsRef.current = dependencies;
}, dependencies || [selector]);
useLayoutEffect(() => {
return subscribe(() => {
// Use the last selector passed to useStore to get current state slice
const selectedSlice = selectStateRef.current(state); // Shallow compare previous state slice with current and rerender only if changed
if (!shallowEqual(stateSliceRef.current, selectedSlice)) dispatch(selectedSlice);
});
}, []);
return selector ? subscribe( // Truthy check because it might be possible to set selectorRef to
() => selectorRef.current ? selectorRef.current(state) : state, dispatch) : subscribe(dispatch); // Only resubscribe to the store when changing selector from function to
// undefined or undefined to function
}, [!selector]);
return stateSlice;
}
};
let state = createState(setState, getState);
const api = {
return [useStore, {
destroy,

@@ -86,6 +99,5 @@ getState,

subscribe
};
return [useStore, api];
}];
}
export default create;
{
"name": "zustand",
"version": "0.1.3",
"version": "0.2.0",
"description": "🐻 Bear necessities for state management in React",

@@ -5,0 +5,0 @@ "main": "dist/cjs/index.js",

@@ -5,2 +5,4 @@ <p align="center">

[![Build Status](https://travis-ci.org/react-spring/zustand.svg?branch=master)](https://travis-ci.org/react-spring/zustand) [![npm version](https://badge.fury.io/js/zustand.svg)](https://badge.fury.io/js/zustand)
npm install zustand

@@ -17,10 +19,10 @@

// Name your store anything you like, but remember, it's a hook!
// You store is a hook! Name it as you like
const [useStore] = create(set => ({
// Everything in here is your state
count: 1,
// You don't have to nest your actions, but makes it easier to fetch them later on
actions: {
// You don't have to nest your actions, but makes it easier to fetch them later on
inc: () => set(state => ({ count: state.count + 1 })), // same semantics as setState
dec: () => set(state => ({ count: state.count - 1 })),
dec: () => set(state => ({ count: state.count - 1 })), // ... it *merges* state
},

@@ -42,8 +44,8 @@ }))

function Controls() {
// "actions" isn't special, we just named it like that to fetch updaters easier
const { inc, dec } = useStore(state => state.actions)
// "actions" isn't special, in this case it makes fetching updaters easier
const actions = useStore(state => state.actions)
return (
<>
<button onClick={inc}>up</button>
<button onClick={dec}>down</button>
<button onClick={actions.inc}>up</button>
<button onClick={actions.dec}>down</button>
</>

@@ -61,3 +63,3 @@ )

```jsx
const data = useStore()
const state = useStore()
```

@@ -70,3 +72,3 @@

```jsx
const { name, age } = useStore(state => ({ name: state.name, age: state.age }))
const { foo, bar } = useStore(state => ({ foo: state.foo, bar: state.bar }))
```

@@ -77,4 +79,4 @@

```jsx
const name = useStore(state => state.name)
const age = useStore(state => state.age)
const foo = useStore(state => state.foo)
const bar = useStore(state => state.bar)
```

@@ -91,2 +93,27 @@

## Memoizing selectors, optimizing performance
Flux stores usually call the selector on every render-pass. Most of the time this isn't much of a problem, but when your selectors are computationally expensive, or when you know the component renders a lot (for instance react-motion calling it 60 times per second for animation purposes) you may want to optimize it.
```js
const foo = useStore(state => state.foo[props.id])
```
In this case the selector `state => state.foo[props.id]` will run on every state change, as well as every time the component renders. This isn't expensive at all, but let's optimize it for arguments sake.
You can either pass a static reference:
```js
const fooSelector = useCallback(state => state.foo[props.id], [props.id])
const foo = useStore(fooSelector)
```
Or an optional dependencies array to let Zustand know when the selector updates:
```js
const foo = useStore(state => state.foo[props.id], [props.id])
```
From there on your selector will only run when either state changes, or the selector itself.
## Async actions

@@ -154,8 +181,7 @@

const reducer = (state, { type, ...payload }) => {
const reducer = (state, { type, by = 1 }) => {
switch (type) {
case types.increase: return { ...state, count: state.count + 1 }
case types.decrease: return { ...state, count: state.count - 1 }
case types.increase: return { count: state.count + by }
case types.decrease: return { count: state.count - by }
}
return state
}

@@ -169,3 +195,3 @@

const dispatch = useStore(state => state.dispatch)
dispatch({ type: types.increase })
dispatch({ type: types.increase, by: 2 })
```

@@ -181,10 +207,12 @@

// Getting fresh state
const n = api.getState().n
const num = api.getState().n
// Listening to changes
const unsub = api.subscribe(state => console.log(state.n))
// And with a selector
const unsub2 = api.subscribe(state => state.n, n => console.log(n))
// Updating state, will trigger listeners
api.setState({ n: 1 })
// Unsubscribing handler
// Unsubscribing listener
unsub()
// Destroying the store
// Destroying the store (removing all listeners)
api.destroy()

@@ -207,36 +235,1 @@ ```

```
## Memoizing selectors, performance concerns, etc. (this is just additional info)
Zustand tries to be as performant as possible while still being flexible but there are limitations. This is an attempt to breakdown how Zustand works to enable better estimations of the computational cost.
A component is always subscribed to the part of the store that the latest selector returned:
```js
const foo = useStore(state => state.foo) // subscribed only to state.foo
```
The selector is called first to return the selected state and again on ANY modification to the store, even updates made to a different part of the store:
```js
const [useStore, { setState }] = create(() => ({ foo: 'foo', bar: 'bar' }))
function ComponentFoo() {
return useStore(state => state.foo)
}
function ComponentBar() {
return useStore(state => state.bar)
}
setState({ bar: 'new bar' }) // All selectors are called but only ComponentBar renders again
```
Zustand calls selectors to compare the selected state (the return value of the selector) with the previous selected state. An update is dispatched to the component if the new selected state is different. The comparison is done with a shallow equality check. The component will then render again with the new selected state. Zustand has to check if the selector is new during the re render because it can be changed at any time. If the selector is new, it's called and the return value is used instead of the selected state that was dispatched.
It's best to use selectors that are not computationally expensive as they are called on every update to the store. You can also skip the additional call to the selector by extracting the selector and passing it in as a static reference:
```js
const fooSelector = state => state.foo
const foo = useStore(fooSelector) // fooSelector only called on initialization and store updates
```
You can also pass an optional dependencies array to let Zustand know when the selector updates:
```js
// selector only called on initialization, store updates, and props.key updates
const part = useStore(state => state[props.key], [props.key])
```
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc