New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

pullstate

Package Overview
Dependencies
Maintainers
1
Versions
99
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pullstate - npm Package Compare versions

Comparing version 1.1.1 to 1.2.0

dist/InjectStoreStateOpt.d.ts

12

Changelog.md

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

## 1.2.0
New experimental optimized updates (uses immer patches internally). To use, your state selections need to be made using paths - and make use of the new methods and components `useStoreStateOpt` and `<InjectStoreStateOpt>` respectively.
Instead of passing a function, you now pass an array of path selections. The state returned will be an array of values per each state selection path. E.g:
```ts
const [isDarkMode] = useStoreStateOpt(UIStore, [["isDarkMode"]])
```
The performance benefits stem from Pullstate not having to run equality checks on the results of your selected state and then re-render your component accordingly, but instead looks at the immer update patches directly for which paths changed in your state and re-renders the listeners on those paths.
## 1.1.0

@@ -2,0 +14,0 @@

4

dist/index.d.ts
import { useStoreState } from "./useStoreState";
import { useStoreStateOpt } from "./useStoreStateOpt";
import { Store, update } from "./Store";

@@ -8,2 +9,3 @@ import { InjectStoreState } from "./InjectStoreState";

import { EAsyncEndTags, TPullstateAsyncAction } from "./async-types";
export { useStoreState, update, Store, InjectStoreState, PullstateProvider, useStores, createPullstateCore, createAsyncAction, successResult, errorResult, EAsyncEndTags, IPullstateInstanceConsumable, InjectAsyncAction, EAsyncActionInjectType, TInjectAsyncActionProps, TPullstateAsyncAction };
import { InjectStoreStateOpt } from "./InjectStoreStateOpt";
export { useStoreState, useStoreStateOpt, update, Store, InjectStoreState, InjectStoreStateOpt, PullstateProvider, useStores, createPullstateCore, createAsyncAction, successResult, errorResult, EAsyncEndTags, IPullstateInstanceConsumable, InjectAsyncAction, EAsyncActionInjectType, TInjectAsyncActionProps, TPullstateAsyncAction, };

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

import React,{useState,useRef,useCallback,useEffect,useContext,useMemo}from'react';import produce$1 from'immer';const isEqual = require("fast-deep-equal");
import React,{useState,useRef,useEffect,useContext,useMemo}from'react';import produce$1 from'immer';const isEqual = require("fast-deep-equal");
function useStoreState(store, getSubState) {

@@ -12,10 +12,9 @@ const [subState, setSubState] = useState(() => getSubState ? getSubState(store.getRawState()) : store.getRawState());

updateRef.current.getSubState = getSubState;
const onStoreUpdate = useCallback(() => {
const nextSubState = updateRef.current.getSubState ? updateRef.current.getSubState(store.getRawState()) : store.getRawState();
if (updateRef.current.shouldUpdate && !isEqual(updateRef.current.currentSubState, nextSubState)) {
setSubState(nextSubState);
}
}, []);
if (updateRef.current.onStoreUpdate === null) {
updateRef.current.onStoreUpdate = onStoreUpdate;
updateRef.current.onStoreUpdate = function onStoreUpdate() {
const nextSubState = updateRef.current.getSubState ? updateRef.current.getSubState(store.getRawState()) : store.getRawState();
if (updateRef.current.shouldUpdate && !isEqual(updateRef.current.currentSubState, nextSubState)) {
setSubState(nextSubState);
}
};
store._addUpdateListener(updateRef.current.onStoreUpdate);

@@ -25,5 +24,39 @@ }

updateRef.current.shouldUpdate = false;
store._removeUpdateListener(onStoreUpdate);
store._removeUpdateListener(updateRef.current.onStoreUpdate);
}, []);
return subState;
}let updateListenerOrd = 0;
function fastGet(obj, path) {
return path.reduce((cur = obj, key) => {
return cur[key];
}, undefined);
}
function getSubStateFromPaths(store, paths) {
const state = store.getRawState();
const resp = [];
for (const path of paths) {
resp.push(fastGet(state, path));
}
return resp;
}
function useStoreStateOpt(store, paths) {
const [subState, setSubState] = useState(() => getSubStateFromPaths(store, paths));
const updateRef = useRef({
shouldUpdate: true,
onStoreUpdate: null,
currentSubState: null,
ordKey: `_${updateListenerOrd++}`,
});
updateRef.current.currentSubState = subState;
if (updateRef.current.onStoreUpdate === null) {
updateRef.current.onStoreUpdate = function onStoreUpdateOpt() {
setSubState(getSubStateFromPaths(store, paths));
};
store._addUpdateListenerOpt(updateRef.current.onStoreUpdate, updateRef.current.ordKey, paths);
}
useEffect(() => () => {
updateRef.current.shouldUpdate = false;
store._removeUpdateListenerOpt(updateRef.current.ordKey);
}, []);
return subState;
}const isEqual$1 = require("fast-deep-equal");

@@ -57,2 +90,3 @@ const Immer = require("immer");

}
const optPathDivider = "~._.~";
class Store {

@@ -65,2 +99,6 @@ constructor(initialState) {

this.reactionCreators = [];
this.optimizedUpdateListeners = {};
this.optimizedUpdateListenerPaths = {};
this.optimizedListenerPropertyMap = {};
this._optListenerCount = 0;
this.currentState = initialState;

@@ -86,3 +124,3 @@ this.initialState = initialState;

}
_updateState(nextState) {
_updateState(nextState, updateKeyedPaths = []) {
this.currentState = nextState;

@@ -97,2 +135,15 @@ for (const runReaction of this.reactions) {

this.updateListeners.forEach(listener => listener());
if (updateKeyedPaths.length > 0) {
const updateOrds = new Set();
for (const keyedPath of updateKeyedPaths) {
if (this.optimizedListenerPropertyMap[keyedPath]) {
for (const ord of this.optimizedListenerPropertyMap[keyedPath]) {
updateOrds.add(ord);
}
}
}
for (const ord of updateOrds.values()) {
this.optimizedUpdateListeners[ord]();
}
}
}

@@ -103,5 +154,28 @@ }

}
_addUpdateListenerOpt(listener, ordKey, paths) {
this.optimizedUpdateListeners[ordKey] = listener;
const listenerPathsKeyed = paths.map(path => path.join(optPathDivider));
this.optimizedUpdateListenerPaths[ordKey] = listenerPathsKeyed;
for (const keyedPath of listenerPathsKeyed) {
if (this.optimizedListenerPropertyMap[keyedPath] == null) {
this.optimizedListenerPropertyMap[keyedPath] = [ordKey];
}
else {
this.optimizedListenerPropertyMap[keyedPath].push(ordKey);
}
}
this._optListenerCount++;
}
_removeUpdateListener(listener) {
this.updateListeners = this.updateListeners.filter(f => f !== listener);
}
_removeUpdateListenerOpt(ordKey) {
const listenerPathsKeyed = this.optimizedUpdateListenerPaths[ordKey];
for (const keyedPath of listenerPathsKeyed) {
this.optimizedListenerPropertyMap[keyedPath] = this.optimizedListenerPropertyMap[keyedPath].filter(ord => ord !== ordKey);
}
delete this.optimizedUpdateListenerPaths[ordKey];
delete this.optimizedUpdateListeners[ordKey];
this._optListenerCount--;
}
subscribe(watch, listener) {

@@ -150,6 +224,33 @@ if (!this.ssr) {

const currentState = store.getRawState();
const nextState = produce(currentState, s => updater(s, currentState), patchesCallback);
if (nextState !== currentState) {
store._updateState(nextState);
if (store._optListenerCount > 0) {
let changePatches;
const nextState = produce(currentState, s => updater(s, currentState), (patches, inversePatches) => {
if (patchesCallback) {
patchesCallback(patches, inversePatches);
}
changePatches = patches;
});
if (changePatches.length > 0) {
const updateKeyedPathsMap = {};
for (const patch of changePatches) {
let curKey;
for (const p of patch.path) {
if (curKey) {
curKey = `${curKey}${optPathDivider}${p}`;
}
else {
curKey = p;
}
updateKeyedPathsMap[curKey] = true;
}
}
store._updateState(nextState, Object.keys(updateKeyedPathsMap));
}
}
else {
const nextState = produce(currentState, s => updater(s, currentState), patchesCallback);
if (nextState !== currentState) {
store._updateState(nextState);
}
}
}function InjectStoreState({ store, on = s => s, children, }) {

@@ -724,2 +825,5 @@ const state = useStoreState(store, on);

return props.children(response);
}export{useStoreState,update,Store,InjectStoreState,PullstateProvider,useStores,createPullstateCore,createAsyncAction,successResult,errorResult,EAsyncEndTags,InjectAsyncAction,EAsyncActionInjectType};
}function InjectStoreStateOpt({ store, paths, children, }) {
const state = useStoreStateOpt(store, paths);
return children(state);
}export{useStoreState,useStoreStateOpt,update,Store,InjectStoreState,InjectStoreStateOpt,PullstateProvider,useStores,createPullstateCore,createAsyncAction,successResult,errorResult,EAsyncEndTags,InjectAsyncAction,EAsyncActionInjectType};

@@ -12,10 +12,9 @@ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});function _interopDefault(e){return(e&&(typeof e==='object')&&'default'in e)?e['default']:e}var React=require('react'),React__default=_interopDefault(React),produce$1=_interopDefault(require('immer'));const isEqual = require("fast-deep-equal");

updateRef.current.getSubState = getSubState;
const onStoreUpdate = React.useCallback(() => {
const nextSubState = updateRef.current.getSubState ? updateRef.current.getSubState(store.getRawState()) : store.getRawState();
if (updateRef.current.shouldUpdate && !isEqual(updateRef.current.currentSubState, nextSubState)) {
setSubState(nextSubState);
}
}, []);
if (updateRef.current.onStoreUpdate === null) {
updateRef.current.onStoreUpdate = onStoreUpdate;
updateRef.current.onStoreUpdate = function onStoreUpdate() {
const nextSubState = updateRef.current.getSubState ? updateRef.current.getSubState(store.getRawState()) : store.getRawState();
if (updateRef.current.shouldUpdate && !isEqual(updateRef.current.currentSubState, nextSubState)) {
setSubState(nextSubState);
}
};
store._addUpdateListener(updateRef.current.onStoreUpdate);

@@ -25,5 +24,39 @@ }

updateRef.current.shouldUpdate = false;
store._removeUpdateListener(onStoreUpdate);
store._removeUpdateListener(updateRef.current.onStoreUpdate);
}, []);
return subState;
}let updateListenerOrd = 0;
function fastGet(obj, path) {
return path.reduce((cur = obj, key) => {
return cur[key];
}, undefined);
}
function getSubStateFromPaths(store, paths) {
const state = store.getRawState();
const resp = [];
for (const path of paths) {
resp.push(fastGet(state, path));
}
return resp;
}
function useStoreStateOpt(store, paths) {
const [subState, setSubState] = React.useState(() => getSubStateFromPaths(store, paths));
const updateRef = React.useRef({
shouldUpdate: true,
onStoreUpdate: null,
currentSubState: null,
ordKey: `_${updateListenerOrd++}`,
});
updateRef.current.currentSubState = subState;
if (updateRef.current.onStoreUpdate === null) {
updateRef.current.onStoreUpdate = function onStoreUpdateOpt() {
setSubState(getSubStateFromPaths(store, paths));
};
store._addUpdateListenerOpt(updateRef.current.onStoreUpdate, updateRef.current.ordKey, paths);
}
React.useEffect(() => () => {
updateRef.current.shouldUpdate = false;
store._removeUpdateListenerOpt(updateRef.current.ordKey);
}, []);
return subState;
}const isEqual$1 = require("fast-deep-equal");

@@ -57,2 +90,3 @@ const Immer = require("immer");

}
const optPathDivider = "~._.~";
class Store {

@@ -65,2 +99,6 @@ constructor(initialState) {

this.reactionCreators = [];
this.optimizedUpdateListeners = {};
this.optimizedUpdateListenerPaths = {};
this.optimizedListenerPropertyMap = {};
this._optListenerCount = 0;
this.currentState = initialState;

@@ -86,3 +124,3 @@ this.initialState = initialState;

}
_updateState(nextState) {
_updateState(nextState, updateKeyedPaths = []) {
this.currentState = nextState;

@@ -97,2 +135,15 @@ for (const runReaction of this.reactions) {

this.updateListeners.forEach(listener => listener());
if (updateKeyedPaths.length > 0) {
const updateOrds = new Set();
for (const keyedPath of updateKeyedPaths) {
if (this.optimizedListenerPropertyMap[keyedPath]) {
for (const ord of this.optimizedListenerPropertyMap[keyedPath]) {
updateOrds.add(ord);
}
}
}
for (const ord of updateOrds.values()) {
this.optimizedUpdateListeners[ord]();
}
}
}

@@ -103,5 +154,28 @@ }

}
_addUpdateListenerOpt(listener, ordKey, paths) {
this.optimizedUpdateListeners[ordKey] = listener;
const listenerPathsKeyed = paths.map(path => path.join(optPathDivider));
this.optimizedUpdateListenerPaths[ordKey] = listenerPathsKeyed;
for (const keyedPath of listenerPathsKeyed) {
if (this.optimizedListenerPropertyMap[keyedPath] == null) {
this.optimizedListenerPropertyMap[keyedPath] = [ordKey];
}
else {
this.optimizedListenerPropertyMap[keyedPath].push(ordKey);
}
}
this._optListenerCount++;
}
_removeUpdateListener(listener) {
this.updateListeners = this.updateListeners.filter(f => f !== listener);
}
_removeUpdateListenerOpt(ordKey) {
const listenerPathsKeyed = this.optimizedUpdateListenerPaths[ordKey];
for (const keyedPath of listenerPathsKeyed) {
this.optimizedListenerPropertyMap[keyedPath] = this.optimizedListenerPropertyMap[keyedPath].filter(ord => ord !== ordKey);
}
delete this.optimizedUpdateListenerPaths[ordKey];
delete this.optimizedUpdateListeners[ordKey];
this._optListenerCount--;
}
subscribe(watch, listener) {

@@ -150,6 +224,33 @@ if (!this.ssr) {

const currentState = store.getRawState();
const nextState = produce(currentState, s => updater(s, currentState), patchesCallback);
if (nextState !== currentState) {
store._updateState(nextState);
if (store._optListenerCount > 0) {
let changePatches;
const nextState = produce(currentState, s => updater(s, currentState), (patches, inversePatches) => {
if (patchesCallback) {
patchesCallback(patches, inversePatches);
}
changePatches = patches;
});
if (changePatches.length > 0) {
const updateKeyedPathsMap = {};
for (const patch of changePatches) {
let curKey;
for (const p of patch.path) {
if (curKey) {
curKey = `${curKey}${optPathDivider}${p}`;
}
else {
curKey = p;
}
updateKeyedPathsMap[curKey] = true;
}
}
store._updateState(nextState, Object.keys(updateKeyedPathsMap));
}
}
else {
const nextState = produce(currentState, s => updater(s, currentState), patchesCallback);
if (nextState !== currentState) {
store._updateState(nextState);
}
}
}function InjectStoreState({ store, on = s => s, children, }) {

@@ -722,2 +823,5 @@ const state = useStoreState(store, on);

return props.children(response);
}exports.useStoreState=useStoreState;exports.update=update;exports.Store=Store;exports.InjectStoreState=InjectStoreState;exports.PullstateProvider=PullstateProvider;exports.useStores=useStores;exports.createPullstateCore=createPullstateCore;exports.createAsyncAction=createAsyncAction;exports.successResult=successResult;exports.errorResult=errorResult;exports.InjectAsyncAction=InjectAsyncAction;
}function InjectStoreStateOpt({ store, paths, children, }) {
const state = useStoreStateOpt(store, paths);
return children(state);
}exports.useStoreState=useStoreState;exports.useStoreStateOpt=useStoreStateOpt;exports.update=update;exports.Store=Store;exports.InjectStoreState=InjectStoreState;exports.InjectStoreStateOpt=InjectStoreStateOpt;exports.PullstateProvider=PullstateProvider;exports.useStores=useStores;exports.createPullstateCore=createPullstateCore;exports.createAsyncAction=createAsyncAction;exports.successResult=successResult;exports.errorResult=errorResult;exports.InjectAsyncAction=InjectAsyncAction;
import { Patch } from "immer";
import { TPath } from "./useStoreStateOpt";
export declare type TPullstateUpdateListener = () => void;

@@ -19,2 +20,6 @@ export interface IStoreInternalOptions<S> {

private reactionCreators;
private optimizedUpdateListeners;
private optimizedUpdateListenerPaths;
private optimizedListenerPropertyMap;
_optListenerCount: number;
constructor(initialState: S);

@@ -26,5 +31,7 @@ _setInternalOptions({ ssr, reactionCreators }: IStoreInternalOptions<S>): void;

_updateStateWithoutReaction(nextState: S): void;
_updateState(nextState: S): void;
_updateState(nextState: S, updateKeyedPaths?: string[]): void;
_addUpdateListener(listener: TPullstateUpdateListener): void;
_addUpdateListenerOpt(listener: TPullstateUpdateListener, ordKey: string, paths: TPath[]): void;
_removeUpdateListener(listener: TPullstateUpdateListener): void;
_removeUpdateListenerOpt(ordKey: string): void;
subscribe<T>(watch: (state: S) => T, listener: (watched: T, allState: S, previousWatched: T) => void): () => void;

@@ -31,0 +38,0 @@ createReaction<T>(watch: (state: S) => T, reaction: TReactionFunction<S, T>): () => void;

import { Store } from "./Store";
export interface IUpdateRef {
shouldUpdate: boolean;
onStoreUpdate: () => void;
getSubState: any;
currentSubState: any;
}
declare function useStoreState<S = any>(store: Store<S>): S;
declare function useStoreState<S = any, SS = any>(store: Store<S>, getSubState: (state: S) => SS): SS;
export { useStoreState };
{
"name": "pullstate",
"version": "1.1.1",
"version": "1.2.0",
"description": "Simple state stores using immer and React hooks",

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

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