Comparing version 0.1.0 to 0.2.0
# Change Log | ||
This project adheres to [Semantic Versioning](http://semver.org/). | ||
## 0.2 | ||
* Re-render components only if related state keys were changed. | ||
* Add state changes to `@changed` event. | ||
* Add `storeon/logger`. | ||
* Remove `mapStateToProps` function support from `connect()`. | ||
* Reduce size. | ||
## 0.1 | ||
* Initial release. |
35
index.js
@@ -1,3 +0,1 @@ | ||
var merge = require('./merge') | ||
/** | ||
@@ -42,20 +40,25 @@ * Initialize new store and apply all modules to the store. | ||
var list = events[event] | ||
if (process.env.NODE_ENV !== 'production') { | ||
if (event.indexOf('@') !== 0 && !list) { | ||
if (event.indexOf('@') !== 0 && !events[event]) { | ||
throw new Error('Unknown event ' + event) | ||
} | ||
} | ||
if (!list) return | ||
var changed = false | ||
list.forEach(function (i) { | ||
var changes = i(state, data) | ||
if (changes) { | ||
state = merge(state, changes) | ||
changed = true | ||
} | ||
}) | ||
if (changed) { | ||
dispatch('@changed') | ||
if (events[event]) { | ||
var changes = { } | ||
var changed, key | ||
events[event].forEach(function (i) { | ||
var diff = i(state, data) | ||
if (diff) { | ||
changed = true | ||
var newState = { } | ||
for (key in diff) { | ||
newState[key] = diff[key] | ||
changes[key] = diff[key] | ||
} | ||
for (key in state) newState[key] = newState[key] || state[key] | ||
state = newState | ||
} | ||
}) | ||
if (changed) dispatch('@changed', changes) | ||
} | ||
@@ -71,3 +74,3 @@ } | ||
modules.forEach(function (i) { | ||
i(store) | ||
if (i) i(store) | ||
}) | ||
@@ -74,0 +77,0 @@ store.dispatch('@init') |
{ | ||
"name": "storeon", | ||
"version": "0.1.0", | ||
"description": "Tiny (196 bytes) event-based Redux-like state manager for React and Preact", | ||
"version": "0.2.0", | ||
"description": "Tiny (186 bytes) event-based Redux-like state manager for React and Preact", | ||
"keywords": [ | ||
@@ -14,2 +14,5 @@ "state", | ||
"repository": "ai/storeon", | ||
"browser": { | ||
"./logger.js": "./logger.browser.js" | ||
}, | ||
"husky": { | ||
@@ -16,0 +19,0 @@ "hooks": { |
var Preact = require('preact') | ||
var hooks = require('preact/hooks') | ||
var integrate = require('./integrate') | ||
var StoreContext = Preact.createContext('storeon') | ||
module.exports = integrate( | ||
Preact.h, | ||
Preact.createContext, | ||
hooks.useState, | ||
hooks.useContext, | ||
hooks.useEffect | ||
) | ||
module.exports = function connect () { | ||
var keys = [].slice.call(arguments, 0, arguments.length - 1) | ||
var Component = arguments[arguments.length - 1] | ||
return function (originProps) { | ||
var store = hooks.useContext(StoreContext) | ||
var rerender = hooks.useState()[1] | ||
var state = store.get() | ||
var props = { } | ||
keys.forEach(function (key) { | ||
props[key] = state[key] | ||
}) | ||
function update (_, changed) { | ||
var changesInKeys = keys.some(function (key) { | ||
return key in changed | ||
}) | ||
if (changesInKeys) rerender({ }) | ||
} | ||
hooks.useEffect(function () { | ||
return store.on('@changed', update) | ||
}, []) | ||
for (var i in originProps) { | ||
if (!(i in props)) props[i] = originProps[i] | ||
} | ||
props.dispatch = store.dispatch | ||
return Preact.h(Component, props) | ||
} | ||
} | ||
module.exports.StoreContext = StoreContext |
78
react.js
var React = require('react') | ||
var integrate = require('./integrate') | ||
/** | ||
* Context to put store for `connect` decorator. | ||
* | ||
* @example | ||
* import { StoreContext } from 'storeon/react' | ||
* render( | ||
* <StoreContext.Provider value={store}><App /></StoreContext.Provider>, | ||
* document.body | ||
* ) | ||
* | ||
* @name StoreContext | ||
* @type {Context} | ||
*/ | ||
var StoreContext = React.createContext('storeon') | ||
module.exports = integrate( | ||
React.createElement, | ||
React.createContext, | ||
React.useState, | ||
React.useContext, | ||
React.useEffect | ||
) | ||
module.exports = function connect () { | ||
var keys = [].slice.call(arguments, 0, arguments.length - 1) | ||
var Component = arguments[arguments.length - 1] | ||
return function (originProps) { | ||
var store = React.useContext(StoreContext) | ||
var rerender = React.useState()[1] | ||
var state = store.get() | ||
var props = { } | ||
keys.forEach(function (key) { | ||
props[key] = state[key] | ||
}) | ||
function update (_, changed) { | ||
var changesInKeys = keys.some(function (key) { | ||
return key in changed | ||
}) | ||
if (changesInKeys) rerender({ }) | ||
} | ||
React.useEffect(function () { | ||
return store.on('@changed', update) | ||
}, []) | ||
for (var i in originProps) { | ||
if (!(i in props)) props[i] = originProps[i] | ||
} | ||
props.dispatch = store.dispatch | ||
return React.createElement(Component, props) | ||
} | ||
} | ||
module.exports.StoreContext = StoreContext | ||
/** | ||
* Connect React/Preact components to the store. | ||
* | ||
* @param {converter|...string} fields List of state’s field or function | ||
* to map state to props. | ||
* @param {function} Component React/Preact component. | ||
* | ||
* @return {function} Wrapped component | ||
* | ||
* @example | ||
* import connect from 'storeon/react' // or 'storeon/preact' | ||
* const Counter = ({ count, dispatch }) => { | ||
* return <div> | ||
* {count} | ||
* <button onClick={() => dispatch('inc')} | ||
* </div> | ||
* } | ||
* export default connect('count', React.memo(Counter)) | ||
* | ||
* @name connect | ||
* @function | ||
*/ |
@@ -5,4 +5,6 @@ # Storeon | ||
* **Small.** 196 bytes (minified and gzipped). No dependencies. | ||
* **Small.** 186 bytes (minified and gzipped). No dependencies. | ||
It uses [Size Limit] to control size. | ||
* **Fast.** It track what state parts was changed and re-render only components | ||
based on this state parts. | ||
* **Immutable.** The same Redux reducers, but already with syntax sugar on top. | ||
@@ -18,3 +20,3 @@ * **Modular.** API created to move business logic away from React components. | ||
store.on('@init', () => ({ count: 0 })) | ||
// Reducers can return only changed part of the state | ||
// Reducers returns only changed part of the state | ||
store.on('inc', ({ count }) => ({ count: count + 1 })) | ||
@@ -27,11 +29,10 @@ } | ||
```js | ||
import { connect } from 'storeon/react' // or storeon/preact | ||
import connect from 'storeon/react' // or storeon/preact | ||
const Counter = ({ count, dispatch }) => { | ||
return <div> | ||
{count} | ||
<button onClick={() => dispatch('inc')} | ||
</div> | ||
} | ||
export default connect('count', React.memo(Counter)) | ||
const Counter = ({ count, dispatch }) => <> | ||
{count} | ||
<button onClick={() => dispatch('inc')} /> | ||
</> | ||
export default connect('count', Counter) | ||
``` | ||
@@ -50,2 +51,4 @@ | ||
[Size Limit]: https://github.com/ai/size-limit | ||
<a href="https://evilmartians.com/?utm_source=storeon"> | ||
@@ -102,4 +105,6 @@ <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" | ||
* `@dispatch` will be fired on every `store.dispatch()` call. | ||
It receives array with event name and event’s data. | ||
Can be useful for debugging. | ||
* `@changed` will be fired every when event listeners changed the state. | ||
It receives object with state changes. | ||
@@ -169,3 +174,3 @@ To add an event listener, call `store.on()` with event name and callback. | ||
```js | ||
import { connect } from 'storeon/react' // Use 'storeon/preact' for Preact | ||
import connect from 'storeon/react' // Use 'storeon/preact' for Preact | ||
@@ -182,18 +187,18 @@ const Users = ({ users, dispatch }) => { | ||
export default connect('users', React.memo(Users)) | ||
export default connect('users', Users) | ||
``` | ||
`connect()` will re-render on any state changes. | ||
It is important for performance to wrap all your component into `React.memo` | ||
or define `shouldComponentUpdate`. | ||
`connect()` accept the list of state keys to pass into `props`. | ||
Or you can pass a function to convert state to `props`. | ||
It will re-render only if this keys will be changed. | ||
### Logger | ||
Storeon has built-in events logger. | ||
```js | ||
const mapToProps = ({ projects }) => ({ | ||
activeProjects: projects.filter(i => i.active) | ||
}) | ||
export default connect(mapToProps, React.memo(Projects, deepEqual)) | ||
const store = createStore([ | ||
… | ||
process.env.NODE_ENV === 'production' && require('storeon/logger') | ||
]) | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
12412
256
198
1
1