From e670ebc0696c498d8071255862ac2dec62c16578 Mon Sep 17 00:00:00 2001 From: Brandon Dail Date: Sat, 14 Apr 2018 20:19:38 -0700 Subject: [PATCH 1/9] Add .travis.yml --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a38333e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +notifications: + email: false +node_js: 'node' \ No newline at end of file From adf88b5682e26fb8c49f7db5e67a774d60c118eb Mon Sep 17 00:00:00 2001 From: Brandon Dail Date: Sat, 14 Apr 2018 20:36:45 -0700 Subject: [PATCH 2/9] Add a few badges to README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 182bdcc..0fcb6d0 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,12 @@ An immutable React state management library with a simple mutable API, memoized [Check out this small demo.](https://codesandbox.io/s/yp34vpk50j) +[![Build Status](https://travis-ci.com/aweary/react-copy-write.svg?branch=master)](https://travis-ci.com/aweary/react-copy-write) +[![npm](https://img.shields.io/npm/v/react-copy-write.svg)](https://www.npmjs.com/package/react-copy-write) +[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/aweary/react-copy-write/blob/master/LICENSE) + + + ## Overview From e89832e9da582c97940e84fd7bea9c6dd795efc7 Mon Sep 17 00:00:00 2001 From: Brandon Dail Date: Sat, 14 Apr 2018 22:40:02 -0700 Subject: [PATCH 3/9] Fix Travis CI badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0fcb6d0..e44968d 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ An immutable React state management library with a simple mutable API, memoized [Check out this small demo.](https://codesandbox.io/s/yp34vpk50j) -[![Build Status](https://travis-ci.com/aweary/react-copy-write.svg?branch=master)](https://travis-ci.com/aweary/react-copy-write) +[![Build Status](https://travis-ci.org/aweary/react-copy-write.svg?branch=master)](https://travis-ci.org/aweary/react-copy-write) [![npm](https://img.shields.io/npm/v/react-copy-write.svg)](https://www.npmjs.com/package/react-copy-write) [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/aweary/react-copy-write/blob/master/LICENSE) From f22fb2bd59b26a527aa2927e656d123b7c326bab Mon Sep 17 00:00:00 2001 From: Justin Hall Date: Sun, 15 Apr 2018 00:13:30 -0600 Subject: [PATCH 4/9] Fix createMutator link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e44968d..91bf285 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ react-copy-write lets you use straightforward mutations to update an immutable s * [Composing Selectors](#composing-selectors) * [Applying Multiple Selectors](#applying-multiple-selectors) * [Updating State](#updating-state) - * [`createUpdater`](#createupdater) + * [`createMutator`](#createmutator) ## Installation From 9e4cace080cdbf07f995e169f7d40e16f0f07430 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 20 Apr 2018 10:15:29 +0800 Subject: [PATCH 5/9] doc: fix undeclared/unused variable in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 91bf285..24842cd 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ An immutable React state management library with a simple mutable API, memoized [Check out this small demo.](https://codesandbox.io/s/yp34vpk50j) [![Build Status](https://travis-ci.org/aweary/react-copy-write.svg?branch=master)](https://travis-ci.org/aweary/react-copy-write) -[![npm](https://img.shields.io/npm/v/react-copy-write.svg)](https://www.npmjs.com/package/react-copy-write) +[![npm](https://img.shields.io/npm/v/react-copy-write.svg)](https://www.npmjs.com/package/react-copy-write) [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/aweary/react-copy-write/blob/master/LICENSE) @@ -295,7 +295,7 @@ const SearchBar = () => (
state.search}> {(search) => ( - + )}
From a4554ed281b50b6f0b0e1adc8de73c8b42dbb597 Mon Sep 17 00:00:00 2001 From: Evan Black Date: Mon, 23 Apr 2018 11:22:10 -0500 Subject: [PATCH 6/9] Fix typo in docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 24842cd..ff31fe5 100644 --- a/README.md +++ b/README.md @@ -261,7 +261,7 @@ const SearchBar = () => ( state.search}> {(search, mutate) => ( mutate(draft => { // Update draft.search (which will end up being state.search) via mutation From 635fc47aa54f582e9d58dca97dd4d294152b9c05 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 24 Apr 2018 15:08:48 +0200 Subject: [PATCH 7/9] Fix typo in the error message --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 4234ab8..9c34906 100644 --- a/src/index.js +++ b/src/index.js @@ -63,7 +63,7 @@ export default function createCopyOnWriteState(baseState: T) { `update(...): you cannot call update when no CopyOnWriteStoreProvider ` + `instance is mounted. Make sure to wrap your consumer components with ` + `the returned Provider, and/or delay your update calls until the component ` + - `tree is moutned.` + `tree is mounted.` ); const nextState = produce(currentState, fn); if (nextState !== currentState) { From 08362657a56907b7bf8c8ce71ab2a40a0b9a8fff Mon Sep 17 00:00:00 2001 From: Pavithra Kodmad Date: Sun, 29 Apr 2018 14:52:51 +1000 Subject: [PATCH 8/9] Add ability to take in middleware --- src/index.js | 130 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 49 deletions(-) diff --git a/src/index.js b/src/index.js index 9c34906..8bb9263 100644 --- a/src/index.js +++ b/src/index.js @@ -45,31 +45,62 @@ function identityFn(n: T): T { return n; } -export default function createCopyOnWriteState(baseState: T) { +function Store(baseState: T) { + let state:T = baseState; + function getState():T { + return state; + } + function setState(newState: T) { + state = newState; + } + return { + getState, + setState + } +} + +export default function createCopyOnWriteState(baseState: T, middlewares = []) { /** * The current state is stored in a closure, shared by the consumers and * the provider. Consumers still respect the Provider/Consumer contract * that React context enforces, by only accessing state in the consumer. */ let currentState: T = baseState; + let store = Store(baseState); let providerListener = null; // $FlowFixMe React.createContext exists now const State = React.createContext(baseState); + + const updateMiddleWare = getState => next => { + console.log('update middleware') + return (fn) => { + const state = getState(); + const nextState = next(state, fn); + if (nextState !== state) { + store.setState(nextState); + providerListener(); + } + }} + + let chainedProduce = produce; + middlewares = middlewares.concat(updateMiddleWare) + middlewares = middlewares.slice() + middlewares.reverse(); + middlewares.forEach(middleware => + chainedProduce = middleware(store.getState)(chainedProduce) + ) + // Wraps immer's produce. Only notifies the Provider // if the returned draft has been changed. function update(fn: UpdateFn) { invariant( providerListener !== null, `update(...): you cannot call update when no CopyOnWriteStoreProvider ` + - `instance is mounted. Make sure to wrap your consumer components with ` + - `the returned Provider, and/or delay your update calls until the component ` + - `tree is mounted.` + `instance is mounted. Make sure to wrap your consumer components with ` + + `the returned Provider, and/or delay your update calls until the component ` + + `tree is mounted.` ); - const nextState = produce(currentState, fn); - if (nextState !== currentState) { - currentState = nextState; - providerListener(); - } + chainedProduce(fn); } /** @@ -77,7 +108,7 @@ export default function createCopyOnWriteState(baseState: T) { * to calling mutate(...) directly, except you can define it statically, * and have any additional arguments forwarded. */ - function createMutator(fn: UpdateFn) { + function createMutator(fn: UpdateFn) { return (...args: mixed[]) => { update(draft => { fn(draft, ...args); @@ -88,14 +119,14 @@ export default function createCopyOnWriteState(baseState: T) { class CopyOnWriteStoreProvider extends React.Component< { children: React$Node }, T - > { + > { state = baseState; componentDidMount() { invariant( providerListener === null, `CopyOnWriteStoreProvider(...): There can only be a single ` + - `instance of a provider rendered at any given time.` + `instance of a provider rendered at any given time.` ); providerListener = this.updateState; } @@ -105,7 +136,7 @@ export default function createCopyOnWriteState(baseState: T) { } updateState = () => { - this.setState(currentState); + this.setState(store.getState()); }; render() { @@ -134,59 +165,60 @@ export default function createCopyOnWriteState(baseState: T) { } render() { - const { children, state } = this.props; - return children(state, update); - } - } + const {children, state } = this.props; + return children(state, update); + } + } class CopyOnWriteConsumer extends React.Component<{ selector: Selector, children: ConsumerCallback }> { - static defaultProps = { - selector: identityFn - }; + static defaultProps = { + selector: identityFn + }; getObservedState(state: T, selectors: Selector): ObservedState { if (Array.isArray(selectors)) { return selectors.map(fn => fn(state)); - } - return selectors(state); - } + } + return selectors(state); + } consumer = (state: T) => { - const { children, selector } = this.props; - const observedState = this.getObservedState(state, selector); - return ( + const {children, selector } = this.props; + const observedState = this.getObservedState(state, selector); + return ( - {children} - - ); - }; + {children} + + ); + }; render() { return {this.consumer}; - } - } - - /** - * A mutator is like a consumer, except that it doesn't actually use any - * of the state. It's used for cases where a component wants to update some - * state, but doesn't care about what the current state is - */ + } + } + + /** + * A mutator is like a consumer, except that it doesn't actually use any + * of the state. It's used for cases where a component wants to update some + * state, but doesn't care about what the current state is + */ class CopyOnWriteMutator extends React.Component<{ children: (Updater) => React$Node }> { - render() { - return this.props.children(update); - } - } + render() { + return this.props.children(update); + } + } return { - Provider: CopyOnWriteStoreProvider, - Consumer: CopyOnWriteConsumer, - Mutator: CopyOnWriteMutator, - update, - createMutator - }; -} + Provider: CopyOnWriteStoreProvider, + Consumer: CopyOnWriteConsumer, + Mutator: CopyOnWriteMutator, + update, + createMutator, + getState: store.getState + }; + } From 8c99d67a16cd8ab7a7fd772b13fe360ce436466e Mon Sep 17 00:00:00 2001 From: Pavithra Kodmad Date: Sun, 29 Apr 2018 14:58:10 +1000 Subject: [PATCH 9/9] Prettify --- src/index.js | 115 ++++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/src/index.js b/src/index.js index 8bb9263..1d99b0d 100644 --- a/src/index.js +++ b/src/index.js @@ -46,8 +46,8 @@ function identityFn(n: T): T { } function Store(baseState: T) { - let state:T = baseState; - function getState():T { + let state: T = baseState; + function getState(): T { return state; } function setState(newState: T) { @@ -56,10 +56,13 @@ function Store(baseState: T) { return { getState, setState - } + }; } -export default function createCopyOnWriteState(baseState: T, middlewares = []) { +export default function createCopyOnWriteState( + baseState: T, + middlewares = [] +) { /** * The current state is stored in a closure, shared by the consumers and * the provider. Consumers still respect the Provider/Consumer contract @@ -71,24 +74,22 @@ export default function createCopyOnWriteState(baseState: T, middlewares = [] // $FlowFixMe React.createContext exists now const State = React.createContext(baseState); - const updateMiddleWare = getState => next => { - console.log('update middleware') - return (fn) => { + const updateMiddleWare = getState => next => fn => { const state = getState(); const nextState = next(state, fn); if (nextState !== state) { store.setState(nextState); providerListener(); } - }} + }; let chainedProduce = produce; - middlewares = middlewares.concat(updateMiddleWare) - middlewares = middlewares.slice() + middlewares = middlewares.concat(updateMiddleWare); + middlewares = middlewares.slice(); middlewares.reverse(); - middlewares.forEach(middleware => - chainedProduce = middleware(store.getState)(chainedProduce) - ) + middlewares.forEach( + middleware => (chainedProduce = middleware(store.getState)(chainedProduce)) + ); // Wraps immer's produce. Only notifies the Provider // if the returned draft has been changed. @@ -96,9 +97,9 @@ export default function createCopyOnWriteState(baseState: T, middlewares = [] invariant( providerListener !== null, `update(...): you cannot call update when no CopyOnWriteStoreProvider ` + - `instance is mounted. Make sure to wrap your consumer components with ` + - `the returned Provider, and/or delay your update calls until the component ` + - `tree is mounted.` + `instance is mounted. Make sure to wrap your consumer components with ` + + `the returned Provider, and/or delay your update calls until the component ` + + `tree is mounted.` ); chainedProduce(fn); } @@ -112,21 +113,21 @@ export default function createCopyOnWriteState(baseState: T, middlewares = [] return (...args: mixed[]) => { update(draft => { fn(draft, ...args); - }) - } + }); + }; } class CopyOnWriteStoreProvider extends React.Component< { children: React$Node }, T - > { + > { state = baseState; componentDidMount() { invariant( providerListener === null, `CopyOnWriteStoreProvider(...): There can only be a single ` + - `instance of a provider rendered at any given time.` + `instance of a provider rendered at any given time.` ); providerListener = this.updateState; } @@ -165,60 +166,60 @@ export default function createCopyOnWriteState(baseState: T, middlewares = [] } render() { - const {children, state } = this.props; - return children(state, update); - } - } + const { children, state } = this.props; + return children(state, update); + } + } class CopyOnWriteConsumer extends React.Component<{ selector: Selector, children: ConsumerCallback }> { - static defaultProps = { - selector: identityFn - }; + static defaultProps = { + selector: identityFn + }; getObservedState(state: T, selectors: Selector): ObservedState { if (Array.isArray(selectors)) { return selectors.map(fn => fn(state)); - } - return selectors(state); - } + } + return selectors(state); + } consumer = (state: T) => { - const {children, selector } = this.props; - const observedState = this.getObservedState(state, selector); - return ( + const { children, selector } = this.props; + const observedState = this.getObservedState(state, selector); + return ( - {children} - - ); - }; + {children} + + ); + }; render() { return {this.consumer}; - } - } - - /** - * A mutator is like a consumer, except that it doesn't actually use any - * of the state. It's used for cases where a component wants to update some - * state, but doesn't care about what the current state is - */ + } + } + + /** + * A mutator is like a consumer, except that it doesn't actually use any + * of the state. It's used for cases where a component wants to update some + * state, but doesn't care about what the current state is + */ class CopyOnWriteMutator extends React.Component<{ children: (Updater) => React$Node }> { - render() { - return this.props.children(update); - } - } + render() { + return this.props.children(update); + } + } return { - Provider: CopyOnWriteStoreProvider, - Consumer: CopyOnWriteConsumer, - Mutator: CopyOnWriteMutator, - update, - createMutator, - getState: store.getState - }; - } + Provider: CopyOnWriteStoreProvider, + Consumer: CopyOnWriteConsumer, + Mutator: CopyOnWriteMutator, + update, + createMutator, + getState: store.getState + }; +}