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

hodux

v1.0.2

Published

The reactivity state management for React

Downloads

11

Readme

English | 简体中文

Hodux

NPM version Build Status Coverage Status NPM downloads size React

The reactivity state management for React. Made with :heart: and ES6 Proxy API.

Inspired by react-easy-state but more friendly for React Hooks.

Features

  • Observable store: the state flow is easy enough.
  • State selectable: extract state as needed, the components will not re-render unless any selected state changes.
  • Perfectly TypeScript support.

Introduction

Hodux is a reactivity state management solution for React which supports both Hooks and Class, it has only 2 core APIs and quit easy to learn.

import { store, useSelector } from 'hodux';

// create store(Observable)
const counter = store({
  num: 0,
  inc() { counter.num += 1; }
});

// select state from store(Dependency collection)
export default function Counter(props) {
  const num = useSelector(() => counter.num);
  // or you can do some compute in component
  // const total = useSelector(() => counter.num + props.step);

  return <div onClick={counter.inc}>The num:{num}</div>;
}

Install

npm install --save hodux
# or
$ yarn add hodux

API

store(model)

  • Signature: function store<M extends object>(model: M): M
  • Description: creates and returns a proxied observable object by the passed model(object), the original model object is not modified. It's just a wrapper of observable().

create store with object-based model:

// stores/counter.js
const counter = store({
  count: 0,
  inc() {
    counter.count++;
  },
  async incx() {
    await wait(1000);
    counter.count += 1;
  }
});

export default counter;

lazy creates:

// stores/counter.js
export default (initalCount = 0) => {
  const state = store({ count: initalCount });

  function inc() {
    state += n;
  }

  async function incx() {
    await wait(1000);
    state.count += 1;
  }

  return { state, inc, incx }
}

local store(create store inner components):

Maybe use native APIs(useState or useReducer) will be better, the goal of hodux is shared store between components.

export default function Counter() {
  const counter = store({ count: 0 });
  const count = useSelector(() => counter.count);

  return <div onClick={() => counter.count++}>{count}</div>;
}

useSelector(selector, config?)

  • Signature: function useSelector<V>(selector: Selector<V>, config?: Config<V>): V
  • Description: extracts state from store as needed, the components will re-render only if the selected state changes, maybe it's the main difference with react-redux's useSelector(), because react-redux call selector whenever store state changes even not selected at all(react-redux internal decides if makes re-render), so you do't need to use any cache selector library(such as reselect) with useSelector.

useSelector accepts two parameters:

  • the first parameter is a selector function which works as observer API in reactivity system. It subscribes the selected state and equals the previous returned value with the next one to decide if or not re-render. Maybe you can do some compute with state in useSelector and takes result as the return value.

  • the second is an optional config object

    • equals: the compare function between previous return value and the next return value, the defalut is equality

    • debugger: the debugger function passed to @nx-js/observer-util

Returns basic type is recommended:

useSelector(() => {
  const items = store.items; // select items from store

  return items.reduce((acc, item) => acc + item.value, 0); // do some compute with state and return result
});

You should pass in equals function when returns object:

useSelector(() => {
  return {
    loading: listStore.loading,
    list: listStore.list
  }
}, { equals: _.equals }); // use lodash/isEqual

Select state from multiple stores:

function CompWithMutlStore() {
  // whenever the `count` from store1 or the `step` from store1 changes the compoent will re-render, so the `result` is always be the newest value
	const result = useSelector(() => store1.count + store2.step);
}

connect(selector, ownProps?)

function connect<V extends {}, OwnProps = {}>(
  selector: Selector<V, OwnProps>,
  config?: Config<V>
): (classComponent: C) => ConnectedComponent<V, OwnProps>

A HOC wrapper of useSelector to connect store state to the class components, and is only for class components.

connect accepts two parameters:

  • selectorWithProps(ownProps?): familiar to selector, but the difference is selectorWithProps must return object type(such as mapStateToProps in react-redux), selectorWithProps accepts the connected component's props as parameter.

  • config: same as useSelector's config parameter

class component usage:

const counter = store({
  n: 0,
  inc() { counter.n += 1; }
});

const selectToProps = () => ({ n: counter.n });

class Counter extends Component {
 render() {
   return <div onClick={counter.inc}>{n}</div>;
 }
}

export default const ReactivedCounter = connect(selectToProps)(Counter);

ownProps:

const selectToProps = (props) => ({
  step: props.step,
  n: testStore.n
});

class Counter extends React.Component {
  state = { n: this.props.n }
  inc() {
    const n = this.state.n + this.props.step;
    this.setState({ n });
  }
  render() {
    return <div onClick={() => this.inc()}>{this.state.n}</div>;
  }
}

const Connected = connect(selectToProps)(Counter);

render(<Connected step={10} />);

<HoduxConfig equals={fn} debugger={fn} />

  • Type: React.FunctionComponent<React.PropsWithChildren<Config<any>>>
  • Description: The global config Provider.
function consoleLogger(e) {
  if (e.type !== 'get') {
    console.log(`[${e.type}]`, e.key, e.value);
  }
}

ReactDOM.render(
  <HoduxConfig debugger={consoleLogger}>
    <App />
  </HoduxConfig>,
  document.getElementById('root')
);

batch(fn)

  • Signature: function batch(fn: Function) => void
  • Description: a wrapper of unstable_batchedUpdates, to prevent multiples render caused by multiple store mutations in asynchronous handler such as setTimeout and Promise, etc. If you experience performance issues you can batch changes manually with batch.

NOTE: The React team plans to improve render batching in the future. The batch API may be removed in the future in favor of React's own batching.

const listStore = store({
  loading: false,
  list: []
});

listStore.load = async () => {
  testStore.loading = true;

  const list  = await fetchData();

  batch(() => {
    testStore.loading = false;
    testStore.list = list;
  });
}

Run examples locally

The examples folder contains working examples. You can run one of them with

$ cd examples/[folder] && npm start

then open http://localhost:3000 in your web browser.

License

MIT