Comparing version 1.0.1 to 2.0.0
# Change Log | ||
This project adheres to [Semantic Versioning](http://semver.org/). | ||
## 2.0 | ||
* Move to named exports. | ||
* Rename types to `StoreonStore`, `StoreonModule`. | ||
* Move logger into `storeon/devtools`. | ||
* Reduce size. | ||
## 1.0.1 | ||
@@ -5,0 +11,0 @@ * Better tree-shaking of React/Preact. |
@@ -1,8 +0,8 @@ | ||
import { Store } from '..' | ||
import { StoreonStore, StoreonModule } from '..' | ||
declare const devtools: { | ||
<State>(store: Store<State>): void | ||
(options?: object): <State>(store: Store<State>) => void | ||
export const storeonDevtools: { | ||
<State>(store: StoreonStore<State>): void | ||
(options?: object): <State>(store: StoreonStore<State>) => void | ||
} | ||
export = devtools | ||
export const storeonLogger: StoreonModule<unknown> |
@@ -1,2 +0,42 @@ | ||
export default options => { | ||
let print | ||
if (typeof navigator !== 'undefined' && navigator.product !== 'ReactNative') { | ||
print = (type, name, opts) => { | ||
if (typeof opts !== 'undefined') { | ||
console.log( | ||
'%c' + type + ' %c' + name, | ||
'color: #070', | ||
'color: #070; font-weight: 700', | ||
opts | ||
) | ||
} else { | ||
console.log( | ||
'%c' + type + ' %c' + name, | ||
'color: #070', | ||
'color: #070; font-weight: 700' | ||
) | ||
} | ||
} | ||
} else { | ||
print = (type, name, opts) => { | ||
if (typeof opts !== 'undefined') { | ||
console.log(type, name, opts) | ||
} else { | ||
console.log(type, name) | ||
} | ||
} | ||
} | ||
let storeonLogger = store => { | ||
store.on('@dispatch', (state, data) => { | ||
if (data[0] === '@changed') { | ||
let keys = Object.keys(data[1]).join(', ') | ||
print('changed', keys, state) | ||
} else { | ||
print('action', String(data[0]), data[1]) | ||
} | ||
}) | ||
} | ||
let storeonDevtools = options => { | ||
let isStore = options && options.on && options.dispatch && options.get | ||
@@ -59,1 +99,3 @@ | ||
} | ||
export { storeonDevtools, storeonLogger } |
113
index.d.ts
@@ -6,12 +6,60 @@ type DataTypes<Map, Key extends keyof Map> = | ||
declare namespace createStore { | ||
export type Dispatch<Events> = (<Event extends keyof Events>( | ||
event: Event, ...data: DataTypes<Partial<Events>, Event> | ||
) => void) & {___events: Events} | ||
/** | ||
* Store with application state and event listeners. | ||
*/ | ||
export class StoreonStore<State = unknown, Events = any> { | ||
/** | ||
* Add event listener. | ||
* | ||
* @param event The event name. | ||
* @param handler The event listener. | ||
* @returns The function to remove listener. | ||
*/ | ||
on <Event extends keyof (Events & StoreonEvents<State, Events>)>( | ||
event: Event, | ||
handler: createStoreon.EventHandler<State, Events, Event> | ||
): () => void | ||
/** | ||
* Return current state. You can use this method only to read state. | ||
* Any state changes should be in event listeners. | ||
* | ||
* @returns The current state. | ||
*/ | ||
get(): State | ||
/** | ||
* Emit event. | ||
* | ||
* @param event The event name. | ||
* @param data Any additional data for the event. | ||
* @returns The current state. | ||
*/ | ||
dispatch: StoreonDispatch< | ||
Events & createStoreon.DispatchableEvents<State> | ||
> | ||
} | ||
export type StoreonModule<State, Events = any> = | ||
(store: StoreonStore<State, Events>) => void | ||
export interface StoreonEvents< | ||
State, Events = any | ||
> extends createStoreon.DispatchableEvents<State>{ | ||
'@dispatch': createStoreon.DispatchEvent< | ||
State, Events & createStoreon.DispatchableEvents<State> | ||
> | ||
} | ||
export type StoreonDispatch<Events> = (<Event extends keyof Events>( | ||
event: Event, ...data: DataTypes<Partial<Events>, Event> | ||
) => void) & {___events: Events} | ||
export namespace createStoreon { | ||
export type DispatchEvent< | ||
State, Events, Event extends keyof Events = keyof Events | ||
> = [Event, Events[Event], Array<StoreonEventHandler<State, Events, Event>>] | ||
> = [Event, Events[Event], Array<EventHandler<State, Events, Event>>] | ||
export type StoreonEventHandler< | ||
export type EventHandler< | ||
State, Events, Event extends keyof (Events & StoreonEvents<State, Events>) | ||
@@ -23,39 +71,2 @@ > = ( | ||
/** | ||
* Store with application state and event listeners. | ||
*/ | ||
export class Store<State = unknown, Events = any> { | ||
/** | ||
* Add event listener. | ||
* | ||
* @param event The event name. | ||
* @param handler The event listener. | ||
* @returns The function to remove listener. | ||
*/ | ||
on <Event extends keyof (Events & StoreonEvents<State, Events>)>( | ||
event: Event, | ||
handler: StoreonEventHandler<State, Events, Event> | ||
): () => void | ||
/** | ||
* Return current state. You can use this method only to read state. | ||
* Any state changes should be in event listeners. | ||
* | ||
* @returns The current state. | ||
*/ | ||
get(): State | ||
/** | ||
* Emit event. | ||
* | ||
* @param event The event name. | ||
* @param data Any additional data for the event. | ||
* @returns The current state. | ||
*/ | ||
dispatch: Dispatch<Events & DispatchableEvents<State>> | ||
} | ||
export type Module<State, Events = any> = | ||
(store: Store<State, Events>) => void | ||
export interface DispatchableEvents<State> { | ||
@@ -65,6 +76,2 @@ '@init': never | ||
} | ||
export interface StoreonEvents<State, Events = any> extends DispatchableEvents<State>{ | ||
'@dispatch': DispatchEvent<State, Events & DispatchableEvents<State>> | ||
} | ||
} | ||
@@ -76,3 +83,3 @@ | ||
* ```js | ||
* import createStore from 'storeon' | ||
* import { createStoreon } from 'storeon' | ||
* let increment = store => { | ||
@@ -82,3 +89,3 @@ * store.on('@init', () => ({ count: 0 })) | ||
* } | ||
* const store = createStore([increment]) | ||
* const store = createStoreon([increment]) | ||
* store.get().count //=> 0 | ||
@@ -93,6 +100,4 @@ * store.dispatch('inc') | ||
*/ | ||
declare function createStore<State, Events = any>( | ||
modules: Array<createStore.Module<State, Events> | false> | ||
): createStore.Store<State, Events> | ||
export = createStore | ||
export function createStoreon<State, Events = any>( | ||
modules: Array<StoreonModule<State, Events> | false> | ||
): StoreonStore<State, Events> |
@@ -1,2 +0,2 @@ | ||
export default modules => { | ||
let createStoreon = modules => { | ||
let events = { } | ||
@@ -43,1 +43,3 @@ let state = { } | ||
} | ||
export { createStoreon } |
{ | ||
"name": "storeon", | ||
"version": "1.0.1", | ||
"description": "Tiny (175 bytes) event-based Redux-like state manager for React and Preact", | ||
"version": "2.0.0", | ||
"description": "Tiny (167 bytes) event-based Redux-like state manager for React and Preact", | ||
"keywords": [ | ||
@@ -40,24 +40,4 @@ "state", | ||
"import": "./react/index.js" | ||
}, | ||
"./devtools/logger": { | ||
"require": "./devtools/logger/index.cjs", | ||
"import": "./devtools/logger/index.js" | ||
}, | ||
"./preact/connect": { | ||
"require": "./preact/connect/index.cjs", | ||
"import": "./preact/connect/index.js" | ||
}, | ||
"./preact/context": { | ||
"require": "./preact/context/index.cjs", | ||
"import": "./preact/context/index.js" | ||
}, | ||
"./react/connect": { | ||
"require": "./react/connect/index.cjs", | ||
"import": "./react/connect/index.js" | ||
}, | ||
"./react/context": { | ||
"require": "./react/context/index.cjs", | ||
"import": "./react/context/index.js" | ||
} | ||
} | ||
} |
@@ -1,6 +0,8 @@ | ||
import { Dispatch } from '..' | ||
import { PreactContext, AnyComponent, FunctionalComponent } from 'preact' | ||
import { StoreonStore, StoreonDispatch } from '..' | ||
declare namespace useStoreon { | ||
export type StoreData<State extends object = {}, EventsMap = any> = { | ||
dispatch: Dispatch<EventsMap> | ||
dispatch: StoreonDispatch<EventsMap> | ||
} & State | ||
@@ -26,6 +28,43 @@ } | ||
*/ | ||
declare function useStoreon<State extends object = {}, EventsMap = any>( | ||
export function useStoreon<State extends object = {}, EventsMap = any>( | ||
...keys: (keyof State)[] | ||
): useStoreon.StoreData<State, EventsMap> | ||
export = useStoreon | ||
/** | ||
* Context to put store for `connect` decorator. | ||
* | ||
* ```js | ||
* import { StoreContext } from 'storeon/preact' | ||
* render( | ||
* <StoreContext.Provider value={store}><App /></StoreContext.Provider>, | ||
* document.body | ||
* ) | ||
* ``` | ||
*/ | ||
export const StoreContext: PreactContext<StoreonStore> | ||
declare namespace connectStoreon { | ||
export type Omit<T, K> = Pick<T, Exclude<keyof T, K>> | ||
export type ConnectedComponent<ComponentProps> = FunctionalComponent< | ||
Partial<Omit<ComponentProps, "dispatch">> | ||
> | ||
} | ||
/** | ||
* Connect Preact components to the store. | ||
* | ||
* ```js | ||
* import connect from 'storeon/preact/connect' | ||
* const Counter = ({ count, dispatch }) => { | ||
* return <div> | ||
* {count} | ||
* <button onClick={() => dispatch('inc')} | ||
* </div> | ||
* } | ||
* export default connect('count', Counter) | ||
* ``` | ||
* | ||
* @returns Wrapped component. | ||
*/ | ||
export function connectStoreon<ComponentProps>( | ||
...keysOrComponent: Array<PropertyKey | AnyComponent<ComponentProps>> | ||
): connectStoreon.ConnectedComponent<ComponentProps> |
import { | ||
useMemo, useContext, useState, useLayoutEffect, useEffect | ||
} from 'preact/hooks' | ||
import { createContext, h } from 'preact' | ||
import StoreContext from './context/index.js' | ||
let StoreContext = createContext() | ||
@@ -10,3 +11,3 @@ let useIsomorphicLayoutEffect = | ||
export default (...keys) => { | ||
let useStoreon = (...keys) => { | ||
let store = useContext(StoreContext) | ||
@@ -39,1 +40,11 @@ if (process.env.NODE_ENV !== 'production' && !store) { | ||
} | ||
let connectStoreon = (...keys) => { | ||
let Component = keys.pop() | ||
return originProps => { | ||
let props = { ...originProps, ...useStoreon(...keys) } | ||
return h(Component, props) | ||
} | ||
} | ||
export { useStoreon, StoreContext, connectStoreon } |
@@ -1,6 +0,8 @@ | ||
import { Dispatch } from '..' | ||
import { Context, ComponentType, FunctionComponent } from 'react' | ||
declare namespace useStoreon { | ||
import { StoreonStore, StoreonDispatch } from '..' | ||
export namespace useStoreon { | ||
export type StoreData<State extends object = {}, EventsMap = any> = { | ||
dispatch: Dispatch<EventsMap> | ||
dispatch: StoreonDispatch<EventsMap> | ||
} & State | ||
@@ -26,6 +28,45 @@ } | ||
*/ | ||
declare function useStoreon<State extends object = {}, EventsMap = any>( | ||
export function useStoreon<State extends object = {}, EventsMap = any>( | ||
...keys: (keyof State)[] | ||
): useStoreon.StoreData<State, EventsMap> | ||
export = useStoreon | ||
/** | ||
* Context to put store for `connect` decorator. | ||
* | ||
* ```js | ||
* import { StoreContext } from 'storeon/react' | ||
* render( | ||
* <StoreContext.Provider value={store}><App /></StoreContext.Provider>, | ||
* document.body | ||
* ) | ||
* ``` | ||
*/ | ||
export const StoreContext: Context<StoreonStore> | ||
declare namespace connectStoreon { | ||
export type Omit<T, K> = Pick<T, Exclude<keyof T, K>> | ||
export type ConnectedComponent<ComponentProps> = FunctionComponent< | ||
Partial<Omit<ComponentProps, "dispatch">> | ||
> | ||
} | ||
/** | ||
* Connect React components to the store. | ||
* | ||
* ```js | ||
* import connect from 'storeon/react/connect' | ||
* const Counter = ({ count, dispatch }) => { | ||
* return <div> | ||
* {count} | ||
* <button onClick={() => dispatch('inc')} | ||
* </div> | ||
* } | ||
* export default connect('count', Counter) | ||
* ``` | ||
* | ||
* @returns Wrapped component. | ||
*/ | ||
export function connectStoreon<ComponentProps>( | ||
...keysOrComponent: Array<PropertyKey | ComponentType<ComponentProps>> | ||
): connectStoreon.ConnectedComponent<ComponentProps> |
import { | ||
useMemo, useContext, useState, useLayoutEffect, useEffect | ||
useMemo, useContext, useState, useLayoutEffect, useEffect, | ||
createContext, createElement | ||
} from 'react' | ||
import StoreContext from './context/index.js' | ||
let StoreContext = createContext() | ||
@@ -10,3 +11,3 @@ let useIsomorphicLayoutEffect = | ||
export default (...keys) => { | ||
let useStoreon = (...keys) => { | ||
let store = useContext(StoreContext) | ||
@@ -39,1 +40,11 @@ if (process.env.NODE_ENV !== 'production' && !store) { | ||
} | ||
let connectStoreon = (...keys) => { | ||
let Component = keys.pop() | ||
return originProps => { | ||
let props = { ...originProps, ...useStoreon(...keys) } | ||
return createElement(Component, props) | ||
} | ||
} | ||
export { useStoreon, StoreContext, connectStoreon } |
@@ -9,3 +9,3 @@ # Storeon | ||
* **Small.** 172 bytes (minified and gzipped). No dependencies. | ||
* **Small.** 167 bytes (minified and gzipped). No dependencies. | ||
It uses [Size Limit] to control size. | ||
@@ -20,3 +20,3 @@ * **Fast.** It tracks what parts of state were changed and re-renders | ||
```js | ||
import createStore from 'storeon' | ||
import { createStoreon } from 'storeon' | ||
@@ -31,9 +31,9 @@ // Initial state, reducers and business logic are packed in independent modules | ||
export const store = createStore([increment]) | ||
export const store = createStoreon([increment]) | ||
``` | ||
```js | ||
import useStoreon from 'storeon/react' // or storeon/preact | ||
import { useStoreon } from 'storeon/react' // or storeon/preact | ||
export default const Counter = () => { | ||
export const Counter = () => { | ||
// Counter will be re-render only on `state.count` changes | ||
@@ -46,3 +46,3 @@ const { dispatch, count } = useStoreon('count') | ||
```js | ||
import StoreContext from 'storeon/react/context' | ||
import { StoreContext } from 'storeon/react' | ||
@@ -112,3 +112,3 @@ render( | ||
The store should be created with `createStore()` function. It accepts a list | ||
The store should be created with `createStoreon()` function. It accepts a list | ||
of the modules. | ||
@@ -121,8 +121,8 @@ | ||
// store/index.js | ||
import createStore from 'storeon' | ||
import { createStoreon } from 'storeon' | ||
import projects from './projects' | ||
import users from './users' | ||
import { projects } from './projects' | ||
import { users } from './users' | ||
export const store = createStore([projects, users]) | ||
export const store = createStoreon([projects, users]) | ||
``` | ||
@@ -133,3 +133,3 @@ | ||
export default store => { | ||
export function projects (store) { | ||
store.on('@init', () => ({ projects: [] })) | ||
@@ -154,7 +154,7 @@ | ||
* `@init` will be fired in `createStore`. The best moment to set | ||
* `@init` will be fired in `createStoreon`. The best moment to set | ||
an initial state. | ||
* `@dispatch` will be fired on every new action (on `store.dispatch()` calls and `@changed` event). | ||
It receives an array with the event name and the event’s data. | ||
Can be useful for debugging. | ||
* `@dispatch` will be fired on every new action (on `store.dispatch()` calls | ||
and `@changed` event). It receives an array with the event name | ||
and the event’s data. Can be useful for debugging. | ||
* `@changed` will be fired when any event changes the state. | ||
@@ -226,3 +226,4 @@ It receives object with state changes. | ||
```js | ||
import useStoreon from 'storeon/react' // Use 'storeon/preact' for Preact | ||
import { useStoreon } from 'storeon/react' // Use 'storeon/preact' for Preact | ||
const Users = () => { | ||
@@ -240,6 +241,6 @@ const { dispatch, users, projects } = useStoreon('users', 'projects') | ||
For class components, you can use `connect()` decorator. | ||
For class components, you can use `connectStoreon()` decorator. | ||
```js | ||
import connect from 'storeon/react/connect' // Use 'storeon/preact/connect' for Preact | ||
import { connectStoreon } from 'storeon/react' // Use 'storeon/preact' for Preact | ||
@@ -258,6 +259,6 @@ class Users extends React.Component { | ||
export default connect('users', 'anotherStateKey', Users) | ||
export default connectStoreon('users', 'anotherStateKey', Users) | ||
``` | ||
`useStoreon` hook and `connect()` accept the list of state keys to pass | ||
`useStoreon` hook and `connectStoreon()` accept the list of state keys to pass | ||
into `props`. It will re-render only if this keys will be changed. | ||
@@ -271,7 +272,7 @@ | ||
```js | ||
import devtools from 'storeon/devtools'; | ||
import { storeonDevtools } from 'storeon/devtools'; | ||
const store = createStore([ | ||
const store = createStoreon([ | ||
… | ||
process.env.NODE_ENV !== 'production' && devtools | ||
process.env.NODE_ENV !== 'production' && storeonDevtools | ||
]) | ||
@@ -287,7 +288,7 @@ ``` | ||
```js | ||
import logger from 'storeon/devtools/logger'; | ||
import { storeonLogger } from 'storeon/devtools'; | ||
const store = createStore([ | ||
const store = createStoreon([ | ||
… | ||
process.env.NODE_ENV !== 'production' && logger | ||
process.env.NODE_ENV !== 'production' && storeonLogger | ||
]) | ||
@@ -308,4 +309,4 @@ ``` | ||
```typescript | ||
import createStore, { Module } from 'storeon' | ||
import useStoreon from 'storeon/react' // or storeon/preact | ||
import { createStoreon, StoreonModule } from 'storeon' | ||
import { useStoreon } from 'storeon/react' // or storeon/preact | ||
@@ -325,3 +326,3 @@ // State structure | ||
const counterModule: Module<State, Events> = store => { | ||
const counterModule: StoreonModule<State, Events> = store => { | ||
store.on('@init', () => ({ counter: 0})) | ||
@@ -332,3 +333,3 @@ store.on('inc', state => ({ counter: state.counter + 1})) | ||
const store = createStore<State, Events>([counterModule]) | ||
const store = createStoreon<State, Events>([counterModule]) | ||
@@ -358,14 +359,2 @@ const Counter = () => { | ||
## ES Modules | ||
Storeon supports ES modules. You do not need to do anything for bundlers. | ||
For quick hacks you can load Storeon from CDN. Do not use it in production | ||
because of low performance. | ||
```js | ||
import createStore from 'https://cdn.jsdelivr.net/npm/storeon/index.js' | ||
``` | ||
## Testing | ||
@@ -381,3 +370,3 @@ | ||
})) | ||
let store = createStore([usersModule]) | ||
let store = createStoreon([usersModule]) | ||
@@ -384,0 +373,0 @@ store.dispatch('users/add', { name: 'User' }) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
33925
19
634
372