Skip to content

Instantly share code, notes, and snippets.

@developit
Last active July 25, 2023 12:54
Show Gist options
  • Save developit/ecd64416d955874c3426d4582045533a to your computer and use it in GitHub Desktop.
Save developit/ecd64416d955874c3426d4582045533a to your computer and use it in GitHub Desktop.

Unistore Hooks for Preact

Experimental hooks-based bindings for Unistore. Available on npm:

npm i unistore-hooks

Note: this should also work with React, just alias "preact" and "preact/hooks" to "react" in your bundler.

Usage

import { h, render } from 'preact';
import createStore from 'unistore';
import { Provider, useStoreState, useActions } from 'unistore-hooks';

const store = createStore({
  count: 0
});

const ACTIONS = {
  increment({ count }) {
    return { count: count + 1 };
  },
  decrement({ count }) {
    return { count: count - 1 };
  },
  setCount({ }, newCount) {
    return { count: newCount };
  }
};

function App(props) {
  // get state values from the store:
  const { count } = useStoreState('count');
  
  // bind + return all actions:
  const { increment, decrement } = useActions(ACTIONS);

  // or create custom bound actions:
  const { reset, add10 } = useActions(ACTIONS, actions => ({
    reset: () => actions.setCount(0),
    add10: () => actions.increment(10)
  }));

  // or declare parameterized actions:
  const max = props.max || 999;
  const { setToMax } = useActions(ACTIONS, actions => ({
    setToMax: [actions.setCount, max] // equivalent to `actions.setCount(max)`
  }), [max]);
  
  return (
    <div>
      <button onClick={decrement}>-1</button>
      Current value: {count}
      <button onClick={increment}>+1</button>
      <button onClick={reset}>Reset</button>
      <button onClick={setToMax}>Max</button>
    </div>
  );
}

render(
  <Provider value={store}>
    <App />
  </Provider>,
  document.body
);
node_modules
package-lock.json
dist
import { createContext } from 'preact';
import { useContext, useMemo, useCallback, useReducer, useEffect } from 'preact/hooks';
// npm.im/dlv
function dlv(t,e,l,n,r){for(e=e.split?e.split('.'):e,n=0;n<e.length;n++)t=t?t[e[n]]:r;return t===r?l:t}
const StoreContext = createContext();
export const Provider = StoreContext.Provider;
function flatUpdate(state, update) {
let changed = false;
for (let i in update) {
if (state[i] !== update[i]) {
if (changed === false) {
changed = true;
state = Object.assign({}, state);
}
state[i] = update[i];
}
}
return state;
}
function createSelector(sel) {
let type = typeof sel;
if (type == 'function') return sel;
if (type == 'string') sel = sel.split(/\s*,\s*/);
if (Array.isArray(sel)) sel = sel.reduce((obj,key)=>((obj[key]=key), obj), {});
return state => {
let selected = {};
if (state) {
for (let key in sel) {
selected[key] = key in state ? state[key] : dlv(state, sel[key]);
}
}
return selected;
};
}
export function useStore() {
return useContext(StoreContext);
}
export function useStoreState(selector) {
const store = useContext(StoreContext);
const filter = useMemo(() => createSelector(selector), []);
const [state, setState] = useReducer(flatUpdate, store ? filter(store.getState()) : {});
useEffect(() => store.subscribe(state => setState(filter(state))), [store]);
return state;
}
function bindActionsToStore(actions, store) {
const bindings = store.bindings || (store.bindings = new (typeof WeakMap=='function' ? WeakMap : Map)());
let bound = bindings.get(actions);
if (!bound) {
bindings.set(actions, bound = {});
if (typeof actions === 'function') actions = actions(store);
for (let i in actions) bound[i] = store.action(actions[i]);
}
return bound;
}
export function useActions(actions, mapping, deps) {
if (typeof deps=='function') ([deps,mapping]=[mapping,deps]);
const store = useContext(StoreContext);
const boundActions = useMemo(
() => bindActionsToStore(actions, store),
[actions, store]
);
mapping = useCallback(mapping, deps || []);
const boundMapping = useMemo(() => {
let a = mapping, map = {};
if (!a) return boundActions;
if (typeof a === 'function') a = a(boundActions);
for (let i in a) {
let v = a[i];
if (Array.isArray(v)) {
v = v[0].bind.apply(v[0], v);
// const f = v[0];
// const a = v;
// v = s => f.apply(this, (a[0]=s, a));
}
// map[i] = store.action(v);
map[i] = v;
}
return map;
}, [boundActions, mapping]);
return boundMapping;
}
{
"name": "unistore-hooks",
"version": "0.1.1",
"main": "dist/unistore-hooks.js",
"module": "dist/unistore-hooks.module.js",
"unpkg": "dist/unistore-hooks.umd.js",
"repo": "https://gist.github.com/developit/ecd64416d955874c3426d4582045533a",
"scripts": {
"prepare": "microbundle index.js --no-sourcemap -f es,cjs",
"prepack": "mv *unistore-hooks.md README.md",
"postpack": "mv README.md *unistore-hooks.md"
},
"devDependencies": {
"microbundle": "^0.12.0",
"preact": "^10.4.4"
},
"peerDependencies": {
"preact": ">=10"
}
}
@danielweck
Copy link

The useEffect import is missing in index.js?
import { useContext, useMemo, useCallback, useReducer } from 'preact/hooks';
It's used in useStoreState() (line 49):
useEffect(() => store.subscribe(state => setState(filter(state))), [store]);

@developit
Copy link
Author

@danielweck - ack! yes, sorry that was a silly mistake. I've just published 0.1.1 to npm with the fix. Thanks!

@tomByrer
Copy link

Seems installing chokes on my machine. & installs WAY to much just for a few functions. I think your npm package has the entire repo, rather than just /dist?
Win10, NodeJS v14.5.0, npm, 6.14.5

$ npm i unistore-hooks
npm WARN deprecated chokidar@2.1.8: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.
npm WARN deprecated fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated core-js@2.6.11: core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.
npm WARN deprecated phantomjs-prebuilt@2.1.16: this package is now deprecated
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated @babel/polyfill@7.8.7: � This package has been deprecated in favor of separate inclusion of a polyfill and regenerator-runtime (when needed). See the @babel/polyfill docs (https://babeljs.io/docs/en/babel-polyfill) for more information.
npm WARN rm not removing D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\.bin\rimraf.cmd as it wasn't installed by D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\rimraf
npm WARN rm not removing D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\.bin\rimraf as it wasn't installed by D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\rimraf
npm WARN rm not removing D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\.bin\semver.cmd as it wasn't installed by D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\semver
npm WARN rm not removing D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\.bin\semver as it wasn't installed by D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\semver
npm WARN rm not removing D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\.bin\mkdirp.cmd as it wasn't installed by D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\mkdirp
npm WARN rm not removing D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\.bin\mkdirp as it wasn't installed by D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\mkdirp
npm WARN rm not removing D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\.bin\json5.cmd as it wasn't installed by D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\json5
npm WARN rm not removing D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\.bin\json5 as it wasn't installed by D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\json5
[            ......] - remove:@uifabric/utilities: sill remove D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\@uifabric\utilities                    [            ......] - remove:@uifabric/[        
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.2.7 (node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.1.2 (node_modules\karma\node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) 
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.1.1 (node_modules\mocha\node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) 
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.2.7 (node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.1.2 (node_modules\karma\node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) 
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.1.1 (node_modules\mocha\node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN notsup Unsupported engine for watchpack-chokidar2@2.0.0: wanted: {"node":"<8.10.0"} (current: {"node":"14.5.0","npm":"6.14.5"})
npm WARN notsup Not compatible with your version of node/npm: watchpack-chokidar2@2.0.0
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.1.2 (node_modules\watchpack\node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN @fluentui/react@7.123.3 requires a peer of @types/react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @fluentui/react@7.123.3 requires a peer of @types/react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @fluentui/react@7.123.3 requires a peer of react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @fluentui/react@7.123.3 requires a peer of react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN office-ui-fabric-react@7.123.4 requires a peer of @types/react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN office-ui-fabric-react@7.123.4 requires a peer of @types/react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN office-ui-fabric-react@7.123.4 requires a peer of react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN office-ui-fabric-react@7.123.4 requires a peer of react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/react-hooks@7.5.4 requires a peer of @types/react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/react-hooks@7.5.4 requires a peer of @types/react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/react-hooks@7.5.4 requires a peer of react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/react-hooks@7.5.4 requires a peer of react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @fluentui/react-focus@7.12.23 requires a peer of @types/react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @fluentui/react-focus@7.12.23 requires a peer of @types/react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @fluentui/react-focus@7.12.23 requires a peer of react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @fluentui/react-focus@7.12.23 requires a peer of react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @fluentui/react-icons@0.1.38 requires a peer of @types/react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @fluentui/react-icons@0.1.38 requires a peer of react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @fluentui/react-icons@0.1.38 requires a peer of react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/utilities@7.24.3 requires a peer of @types/react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/utilities@7.24.3 requires a peer of @types/react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/utilities@7.24.3 requires a peer of react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/utilities@7.24.3 requires a peer of react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/foundation@7.7.37 requires a peer of @types/react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/foundation@7.7.37 requires a peer of @types/react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/foundation@7.7.37 requires a peer of react@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN @uifabric/foundation@7.7.37 requires a peer of react-dom@>=16.8.0 <17.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN babel-plugin-inferno@6.1.0 requires a peer of inferno@>=7 but none is installed. You must install peer dependencies yourself.

npm ERR! code EPERM
npm ERR! syscall rename
npm ERR! path D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\p-retry\package.json.1993313793
npm ERR! dest D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\p-retry\package.json
npm ERR! errno -4048
npm ERR! Error: EPERM: operation not permitted, rename 'D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\p-retry\package.json.1993313793' -> 'D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\p-retry\package.json'
npm ERR!  [OperationalError: EPERM: operation not permitted, rename 'D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\p-retry\package.json.1993313793' -> 'D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\p-retry\package.json'] {
npm ERR!   cause: [Error: EPERM: operation not permitted, rename 'D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\p-retry\package.json.1993313793' -> 'D:\doc\prog\learn\js\quiz-json\quiz-preact-nwb\node_modules\p-retry\package.json'] {
npm ERR!     errno: -4048,
npm ERR!     code: 'EPERM',
npm ERR!     syscall: 'rename',
npm ERR!     path: 'D:\\doc\\prog\\learn\\js\\quiz-json\\quiz-preact-nwb\\node_modules\\p-retry\\package.json.1993313793',
npm ERR!     dest: 'D:\\doc\\prog\\learn\\js\\quiz-json\\quiz-preact-nwb\\node_modules\\p-retry\\package.json'
npm ERR!   },
npm ERR!   errno: -4048,
npm ERR!   code: 'EPERM',
npm ERR!   syscall: 'rename',
npm ERR!   path: 'D:\\doc\\prog\\learn\\js\\quiz-json\\quiz-preact-nwb\\node_modules\\p-retry\\package.json.1993313793',
npm ERR!   dest: 'D:\\doc\\prog\\learn\\js\\quiz-json\\quiz-preact-nwb\\node_modules\\p-retry\\package.json',
npm ERR!   parent: 'quiz-preact-nwb'
npm ERR! }
npm ERR!
npm ERR! The operation was rejected by your operating system.
npm ERR! It's possible that the file was already in use (by a text editor or antivirus),
npm ERR! or that you lack permissions to access it.
npm ERR!
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\tomby\AppData\Roaming\npm-cache\_logs\2020-07-19T13_31_01_102Z-debug.log

@developit
Copy link
Author

@tomBryer - none of that output is related to this package. unistore-hooks is 7 files and has no dependencies:
https://unpkg.com/browse/unistore-hooks@0.1.1/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment