From fec9a03cf7fa86cb783e2fbe865cfaa2ae40d36a Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Mon, 17 Sep 2018 23:05:51 +0200 Subject: [PATCH] Don't create a new object as Context value, as this would trigger unnecessary rerenders of Consumers, just use state for everything. --- src/index.js | 90 +++++++++++++++++++++++++--------------------------- src/spec.js | 24 ++++++++++++++ 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/src/index.js b/src/index.js index 056758c0..ec827f39 100644 --- a/src/index.js +++ b/src/index.js @@ -8,15 +8,26 @@ export const createInstance = (defaultProps = {}) => { const { Consumer, Provider } = React.createContext() class Async extends React.Component { - mounted = false - counter = 0 - args = [] - state = { - data: undefined, - error: undefined, - isLoading: false, - startedAt: undefined, - finishedAt: undefined + constructor(props) { + super(props) + this.mounted = false + this.counter = 0 + this.args = [] + this.state = { + data: undefined, + error: undefined, + isLoading: false, + startedAt: undefined, + finishedAt: undefined, + cancel: this.cancel, + run: this.run, + reload: () => { + this.load() + this.run(...this.args) + }, + setData: this.setData, + setError: this.setError + } } componentDidMount() { @@ -83,27 +94,12 @@ export const createInstance = (defaultProps = {}) => { render() { const { children } = this.props - - const renderProps = { - ...this.state, - cancel: this.cancel, - run: this.run, - reload: () => { - this.load() - this.run(...this.args) - }, - setData: this.setData, - setError: this.setError - } - if (typeof children === "function") { - return {children(renderProps)} + return {children(this.state)} } - if (children !== undefined && children !== null) { - return {children} + return {children} } - return null } } @@ -112,15 +108,15 @@ export const createInstance = (defaultProps = {}) => { * Renders only when deferred promise is pending (not yet run). * * @prop {boolean} persist Show until we have data, even while loading or when an error occurred - * @prop {Function|Node} children Function (passing props) or React node + * @prop {Function|Node} children Function (passing state) or React node */ Async.Pending = ({ children, persist }) => ( - {props => { - if (props.data !== undefined) return null - if (!persist && props.isLoading) return null - if (!persist && props.error !== undefined) return null - return typeof children === "function" ? children(props) : children || null + {state => { + if (state.data !== undefined) return null + if (!persist && state.isLoading) return null + if (!persist && state.error !== undefined) return null + return typeof children === "function" ? children(state) : children || null }} ) @@ -129,14 +125,14 @@ export const createInstance = (defaultProps = {}) => { * Renders only while loading. * * @prop {boolean} initial Show only on initial load (data is undefined) - * @prop {Function|Node} children Function (passing props) or React node + * @prop {Function|Node} children Function (passing state) or React node */ Async.Loading = ({ children, initial }) => ( - {props => { - if (!props.isLoading) return null - if (initial && props.data !== undefined) return null - return typeof children === "function" ? children(props) : children || null + {state => { + if (!state.isLoading) return null + if (initial && state.data !== undefined) return null + return typeof children === "function" ? children(state) : children || null }} ) @@ -145,14 +141,14 @@ export const createInstance = (defaultProps = {}) => { * Renders only when promise is resolved. * * @prop {boolean} persist Show old data while loading - * @prop {Function|Node} children Function (passing data and props) or React node + * @prop {Function|Node} children Function (passing data and state) or React node */ Async.Resolved = ({ children, persist }) => ( - {props => { - if (props.data === undefined) return null - if (props.isLoading && !persist) return null - return typeof children === "function" ? children(props.data, props) : children || null + {state => { + if (state.data === undefined) return null + if (state.isLoading && !persist) return null + return typeof children === "function" ? children(state.data, state) : children || null }} ) @@ -161,14 +157,14 @@ export const createInstance = (defaultProps = {}) => { * Renders only when promise is rejected. * * @prop {boolean} persist Show old error while loading - * @prop {Function|Node} children Function (passing error and props) or React node + * @prop {Function|Node} children Function (passing error and state) or React node */ Async.Rejected = ({ children, persist }) => ( - {props => { - if (props.error === undefined) return null - if (props.isLoading && !persist) return null - return typeof children === "function" ? children(props.error, props) : children || null + {state => { + if (state.error === undefined) return null + if (state.isLoading && !persist) return null + return typeof children === "function" ? children(state.error, state) : children || null }} ) diff --git a/src/spec.js b/src/spec.js index 43a1fe3e..b66d1283 100644 --- a/src/spec.js +++ b/src/spec.js @@ -307,3 +307,27 @@ test("createInstance allows setting default props", async () => { await waitForElement(() => getByText("done")) expect(onResolve).toHaveBeenCalledWith("done") }) + +test("An unrelated change in props does not update the Context", async () => { + let one + let two + const { rerender } = render( + + + {value => { + one = value + }} + + + ) + rerender( + + + {value => { + two = value + }} + + + ) + expect(one).toBe(two) +})