From 625117a9b5c1e0e4f3dc324eef142b6b42724fd0 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Tue, 6 Aug 2019 18:01:19 +0200 Subject: [PATCH 1/7] Fix ambiguous import issue by renaming the standalone helper components. Improved the TS type definitions. --- README.md | 36 ++++----- examples/with-typescript/src/App.tsx | 21 +++++- packages/react-async/src/Async.js | 12 +-- packages/react-async/src/helpers.js | 20 ++--- packages/react-async/src/helpers.spec.js | 62 ++++++++-------- packages/react-async/src/index.d.ts | 94 +++++++++++++++++++----- 6 files changed, 163 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 9fad7b9e..2095f1c5 100644 --- a/README.md +++ b/README.md @@ -267,7 +267,7 @@ by passing in the state, or with `` by using Context. Each of these compo rendering of its children based on the current state. ```jsx -import { useAsync, Pending, Fulfilled, Rejected } from "react-async" +import { useAsync, IfPending, IfFulfilled, IfRejected } from "react-async" const loadCustomer = async ({ customerId }, { signal }) => { // ... @@ -277,16 +277,16 @@ const MyComponent = () => { const state = useAsync({ promiseFn: loadCustomer, customerId: 1 }) return ( <> - Loading... - {error => `Something went wrong: ${error.message}`} - + Loading... + {error => `Something went wrong: ${error.message}`} + {data => (
Loaded some data:
{JSON.stringify(data, null, 2)}
)} -
+ ) } @@ -607,7 +607,7 @@ invoked after the state update is completed. Returns the error to enable chainin React Async provides several helper components that make your JSX more declarative and less cluttered. They don't have to be direct children of `` and you can use the same component several times. -### `` / `` +### `` / `` Renders only while the deferred promise is still waiting to be run, or you have not provided any promise. @@ -622,9 +622,9 @@ Renders only while the deferred promise is still waiting to be run, or you have ```jsx const state = useAsync(...) return ( - +

This text is only rendered while `run` has not yet been invoked on `deferFn`.

-
+
) ``` @@ -650,7 +650,7 @@ return (
``` -### `` / `` +### `` / `` This component renders only while the promise is pending (loading / unsettled). @@ -667,9 +667,9 @@ Alias: `` ```jsx const state = useAsync(...) return ( - +

This text is only rendered while performing the initial load.

-
+
) ``` @@ -683,7 +683,7 @@ return ( {({ startedAt }) => `Loading since ${startedAt.toISOString()}`} ``` -### `` / `` +### `` / `` This component renders only when the promise is fulfilled (resolved to a value, could be `undefined`). @@ -700,9 +700,9 @@ Alias: `` ```jsx const state = useAsync(...) return ( - + {data =>
{JSON.stringify(data)}
} -
+
) ``` @@ -716,7 +716,7 @@ return (
``` -### `` / `` +### `` / `` This component renders only when the promise is rejected. @@ -730,7 +730,7 @@ This component renders only when the promise is rejected. ```jsx const state = useAsync(...) -return Oops. +return Oops. ``` ```jsx @@ -741,7 +741,7 @@ return Oops. {error => `Unexpected error: ${error.message}`} ``` -### `` / `` +### `` / `` This component renders only when the promise is fulfilled or rejected. @@ -755,7 +755,7 @@ This component renders only when the promise is fulfilled or rejected. ```jsx const state = useAsync(...) -return {state => `Finished at ${state.finishedAt.toISOString()}` +return {state => `Finished at ${state.finishedAt.toISOString()}` ``` ## Usage examples diff --git a/examples/with-typescript/src/App.tsx b/examples/with-typescript/src/App.tsx index 5d51e08d..16aa1a9b 100644 --- a/examples/with-typescript/src/App.tsx +++ b/examples/with-typescript/src/App.tsx @@ -1,11 +1,29 @@ import React, { Component } from "react" -import Async, { createInstance } from "react-async" +import Async, { createInstance, useAsync, IfPending, IfRejected, IfFulfilled } from "react-async" import DevTools from "react-async-devtools" import "./App.css" const promiseFn = () => Promise.resolve("baz") const CustomAsync = createInstance({ promiseFn }) +const UseAsync = () => { + const state = useAsync({ promiseFn }) + return ( + <> + Loading... + {error => `Something went wrong: ${error.message}`} + + {data => ( +
+ Loaded some data: +
{JSON.stringify(data, null, 2)}
+
+ )} +
+ + ) +} + class App extends Component { render() { return ( @@ -19,6 +37,7 @@ class App extends Component { {data => <>{data}} + ) diff --git a/packages/react-async/src/Async.js b/packages/react-async/src/Async.js index 45c17dfe..b0d9c99b 100644 --- a/packages/react-async/src/Async.js +++ b/packages/react-async/src/Async.js @@ -1,7 +1,7 @@ import React from "react" import globalScope from "./globalScope" -import { Initial, Pending, Fulfilled, Rejected, Settled } from "./helpers" +import { IfInitial, IfPending, IfFulfilled, IfRejected, IfSettled } from "./helpers" import propTypes from "./propTypes" import { actionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" @@ -201,11 +201,11 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { if (propTypes) Async.propTypes = propTypes.Async - const AsyncInitial = props => {st => } - const AsyncPending = props => {st => } - const AsyncFulfilled = props => {st => } - const AsyncRejected = props => {st => } - const AsyncSettled = props => {st => } + const AsyncInitial = props => {st => } + const AsyncPending = props => {st => } + const AsyncFulfilled = props => {st => } + const AsyncRejected = props => {st => } + const AsyncSettled = props => {st => } AsyncInitial.displayName = `${displayName}.Initial` AsyncPending.displayName = `${displayName}.Pending` diff --git a/packages/react-async/src/helpers.js b/packages/react-async/src/helpers.js index 401fb689..09232f99 100644 --- a/packages/react-async/src/helpers.js +++ b/packages/react-async/src/helpers.js @@ -11,7 +11,7 @@ const renderFn = (children, ...args) => * @prop {Object} state React Async state object * @prop {boolean} persist Show until we have data, even while pending (loading) or when an error occurred */ -export const Initial = ({ children, persist, state = {} }) => +export const IfInitial = ({ children, persist, state = {} }) => state.isInitial || (persist && !state.data) ? renderFn(children, state) : null /** @@ -21,7 +21,7 @@ export const Initial = ({ children, persist, state = {} }) => * @prop {Object} state React Async state object * @prop {boolean} initial Show only on initial load (data is undefined) */ -export const Pending = ({ children, initial, state = {} }) => +export const IfPending = ({ children, initial, state = {} }) => state.isPending && (!initial || !state.value) ? renderFn(children, state) : null /** @@ -31,7 +31,7 @@ export const Pending = ({ children, initial, state = {} }) => * @prop {Object} state React Async state object * @prop {boolean} persist Show old data while pending (promise is loading) */ -export const Fulfilled = ({ children, persist, state = {} }) => +export const IfFulfilled = ({ children, persist, state = {} }) => state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null /** @@ -41,7 +41,7 @@ export const Fulfilled = ({ children, persist, state = {} }) => * @prop {Object} state React Async state object * @prop {boolean} persist Show old error while pending (promise is loading) */ -export const Rejected = ({ children, persist, state = {} }) => +export const IfRejected = ({ children, persist, state = {} }) => state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null /** @@ -51,13 +51,13 @@ export const Rejected = ({ children, persist, state = {} }) => * @prop {Object} state React Async state object * @prop {boolean} persist Show old data or error while pending (promise is loading) */ -export const Settled = ({ children, persist, state = {} }) => +export const IfSettled = ({ children, persist, state = {} }) => state.isSettled || (persist && state.value) ? renderFn(children, state) : null if (propTypes) { - Initial.propTypes = propTypes.Initial - Pending.propTypes = propTypes.Pending - Fulfilled.propTypes = propTypes.Fulfilled - Rejected.propTypes = propTypes.Rejected - Settled.propTypes = propTypes.Settled + IfInitial.propTypes = propTypes.Initial + IfPending.propTypes = propTypes.Pending + IfFulfilled.propTypes = propTypes.Fulfilled + IfRejected.propTypes = propTypes.Rejected + IfSettled.propTypes = propTypes.Settled } diff --git a/packages/react-async/src/helpers.spec.js b/packages/react-async/src/helpers.spec.js index 7799c3cb..58a19b79 100644 --- a/packages/react-async/src/helpers.spec.js +++ b/packages/react-async/src/helpers.spec.js @@ -1,7 +1,7 @@ import "jest-dom/extend-expect" import React from "react" import { render, fireEvent, cleanup, waitForElement } from "@testing-library/react" -import Async, { Initial, Pending, Fulfilled, Rejected, Settled } from "./index" +import Async, { IfInitial, IfPending, IfFulfilled, IfRejected, IfSettled } from "./index" import { resolveIn, resolveTo, rejectTo } from "./specs" afterEach(cleanup) @@ -14,10 +14,10 @@ describe("Fulfilled", () => { {state => ( <> - + {(data, { run }) => } - - {error => error.message} + + {error => error.message} )} @@ -39,10 +39,10 @@ describe("Fulfilled", () => { {state => ( <> - + {(data, { run }) => } - - {error => error.message} + + {error => error.message} )} @@ -57,24 +57,24 @@ describe("Fulfilled", () => { expect(queryByText("fail")).toBeInTheDocument() }) - test("Fulfilled works also with nested Async", async () => { + test("IfFulfilled works also with nested Async", async () => { const outer = () => resolveIn(0)("outer") const inner = () => resolveIn(100)("inner") const { getByText, queryByText } = render( {state => ( - + {outer => ( {state => ( <> - {outer} pending - {inner => outer + " " + inner} + {outer} pending + {inner => outer + " " + inner} )} )} - + )} ) @@ -86,15 +86,15 @@ describe("Fulfilled", () => { }) }) -describe("Pending", () => { +describe("IfPending", () => { test("renders only while the promise is pending", async () => { const promiseFn = () => resolveTo("ok") const { getByText, queryByText } = render( {state => ( <> - pending - done + pending + done )} @@ -105,16 +105,18 @@ describe("Pending", () => { }) }) -describe("Initial", () => { +describe("IfInitial", () => { test("renders only while the deferred promise has not started yet", async () => { const deferFn = () => resolveTo("ok") const { getByText, queryByText } = render( {state => ( <> - {({ run }) => } - pending - done + + {({ run }) => } + + pending + done )} @@ -128,12 +130,12 @@ describe("Initial", () => { }) }) -describe("Rejected", () => { +describe("IfRejected", () => { test("renders only after the promise is rejected", async () => { const promiseFn = () => rejectTo("err") const { getByText, queryByText } = render( - {state => {error => error.message}} + {state => {error => error.message}} ) expect(queryByText("err")).toBeNull() @@ -142,12 +144,12 @@ describe("Rejected", () => { }) }) -describe("Settled", () => { +describe("IfSettled", () => { test("renders after the promise is fulfilled", async () => { const promiseFn = () => resolveTo("value") const { getByText, queryByText } = render( - {state => {({ data }) => data}} + {state => {({ data }) => data}} ) expect(queryByText("value")).toBeNull() @@ -159,7 +161,7 @@ describe("Settled", () => { const promiseFn = () => rejectTo("err") const { getByText, queryByText } = render( - {state => {({ error }) => error.message}} + {state => {({ error }) => error.message}} ) expect(queryByText("err")).toBeNull() @@ -173,14 +175,14 @@ describe("Settled", () => { {state => ( <> - - + + loading - - - +
+ + {({ reload }) => } -
+ )}
diff --git a/packages/react-async/src/index.d.ts b/packages/react-async/src/index.d.ts index 5a930e18..c9215bcb 100644 --- a/packages/react-async/src/index.d.ts +++ b/packages/react-async/src/index.d.ts @@ -1,6 +1,18 @@ -import { Component } from "react" +import React from "react" + +export type AsyncChildren = ((state: AsyncState) => React.ReactNode) | React.ReactNode +export type InitialChildren = ((state: AsyncInitial) => React.ReactNode) | React.ReactNode +export type PendingChildren = ((state: AsyncPending) => React.ReactNode) | React.ReactNode +export type FulfilledChildren = + | ((data: T, state: AsyncFulfilled) => React.ReactNode) + | React.ReactNode +export type RejectedChildren = + | ((error: Error, state: AsyncRejected) => React.ReactNode) + | React.ReactNode +export type SettledChildren = + | ((state: AsyncFulfilled | AsyncRejected) => React.ReactNode) + | React.ReactNode -export type AsyncChildren = ((state: AsyncState) => JSX.Element) | JSX.Element export type PromiseFn = (props: object, controller: AbortController) => Promise export type DeferFn = (args: any[], props: object, controller: AbortController) => Promise @@ -114,39 +126,87 @@ export type AsyncRejected = AbstractState & { } export type AsyncState = AsyncInitial | AsyncPending | AsyncFulfilled | AsyncRejected -export class Async extends Component, AsyncState> {} +export class Async extends React.Component, AsyncState> {} export namespace Async { - export function Initial(props: { children?: AsyncChildren; persist?: boolean }): JSX.Element - export function Pending(props: { children?: AsyncChildren; initial?: boolean }): JSX.Element - export function Loading(props: { children?: AsyncChildren; initial?: boolean }): JSX.Element + export function Initial(props: { + children?: InitialChildren + persist?: boolean + }): JSX.Element + export function Pending(props: { + children?: PendingChildren + initial?: boolean + }): JSX.Element + export function Loading(props: { + children?: PendingChildren + initial?: boolean + }): JSX.Element export function Fulfilled(props: { - children?: AsyncChildren + children?: FulfilledChildren persist?: boolean }): JSX.Element export function Resolved(props: { - children?: AsyncChildren + children?: FulfilledChildren persist?: boolean }): JSX.Element export function Rejected(props: { - children?: AsyncChildren + children?: RejectedChildren + persist?: boolean + }): JSX.Element + export function Settled(props: { + children?: SettledChildren persist?: boolean }): JSX.Element - export function Settled(props: { children?: AsyncChildren; persist?: boolean }): JSX.Element } export function createInstance( defaultProps?: AsyncProps ): (new () => Async) & { - Initial(props: { children?: AsyncChildren; persist?: boolean }): JSX.Element - Pending(props: { children?: AsyncChildren; initial?: boolean }): JSX.Element - Loading(props: { children?: AsyncChildren; initial?: boolean }): JSX.Element - Fulfilled(props: { children?: AsyncChildren; persist?: boolean }): JSX.Element - Resolved(props: { children?: AsyncChildren; persist?: boolean }): JSX.Element - Rejected(props: { children?: AsyncChildren; persist?: boolean }): JSX.Element - Settled(props: { children?: AsyncChildren; persist?: boolean }): JSX.Element + Initial(props: { children?: InitialChildren; persist?: boolean }): JSX.Element + Pending(props: { children?: PendingChildren; initial?: boolean }): JSX.Element + Loading(props: { children?: PendingChildren; initial?: boolean }): JSX.Element + Fulfilled(props: { children?: FulfilledChildren; persist?: boolean }): JSX.Element + Resolved(props: { children?: FulfilledChildren; persist?: boolean }): JSX.Element + Rejected(props: { children?: RejectedChildren; persist?: boolean }): JSX.Element + Settled(props: { children?: SettledChildren; persist?: boolean }): JSX.Element } +export function IfInitial(props: { + children?: InitialChildren + persist?: boolean + state: AsyncState +}): JSX.Element +export function IfPending(props: { + children?: PendingChildren + initial?: boolean + state: AsyncState +}): JSX.Element +export function IfLoading(props: { + children?: PendingChildren + initial?: boolean + state: AsyncState +}): JSX.Element +export function IfFulfilled(props: { + children?: FulfilledChildren + persist?: boolean + state: AsyncState +}): JSX.Element +export function IfResolved(props: { + children?: FulfilledChildren + persist?: boolean + state: AsyncState +}): JSX.Element +export function IfRejected(props: { + children?: RejectedChildren + persist?: boolean + state: AsyncState +}): JSX.Element +export function IfSettled(props: { + children?: SettledChildren + persist?: boolean + state: AsyncState +}): JSX.Element + export function useAsync( arg1: AsyncOptions | PromiseFn, arg2?: AsyncOptions From 28c1f7ae17ad67e2706cc9313c19f06698c25399 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Wed, 7 Aug 2019 16:42:49 +0200 Subject: [PATCH 2/7] Update example to use standalone helpers. --- examples/basic-hook/src/index.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/examples/basic-hook/src/index.js b/examples/basic-hook/src/index.js index 9e87d132..6cafd7fe 100644 --- a/examples/basic-hook/src/index.js +++ b/examples/basic-hook/src/index.js @@ -1,5 +1,5 @@ import React from "react" -import { useAsync } from "react-async" +import { useAsync, IfPending, IfFulfilled, IfRejected } from "react-async" import ReactDOM from "react-dom" import DevTools from "react-async-devtools" import "./index.css" @@ -27,15 +27,16 @@ const UserDetails = ({ data }) => ( ) const User = ({ userId }) => { - const { data, error, isPending } = useAsync({ - promiseFn: loadUser, - debugLabel: `User ${userId}`, - userId, - }) - if (isPending) return - if (error) return

{error.message}

- if (data) return - return null + const state = useAsync({ promiseFn: loadUser, debugLabel: `User ${userId}`, userId }) + return ( + <> + + + + {data => } + {error =>

{error.message}

}
+ + ) } export const App = () => ( From 7dabad121722ab6ac21415975e153fead1c405b1 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Thu, 8 Aug 2019 12:35:48 +0200 Subject: [PATCH 3/7] Add codemods for breaking API changes. --- README.md | 20 +++++++++++++++-- codemods/README.md | 33 ++++++++++++++++++++++++++++ codemods/v6.js | 54 ++++++++++++++++++++++++++++++++++++++++++++++ codemods/v8.js | 33 ++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 codemods/README.md create mode 100644 codemods/v6.js create mode 100644 codemods/v8.js diff --git a/README.md b/README.md index 2095f1c5..87b96040 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,10 @@ error states, without assumptions about the shape of your data or the type of re [abortable fetch]: https://developers.google.com/web/updates/2017/09/abortable-fetch -> ## Upgrading to v6 +> ## Upgrading to v8 > -> Version 6 comes with a breaking change. See [Upgrading](#upgrading) for details. +> Version 8 comes with breaking changes. See [Upgrading](#upgrading) for details. +> A [codemod](https://github.com/ghengeveld/react-async/tree/master/codemods) is available. # Table of Contents @@ -121,11 +122,26 @@ yarn add react-async ### Upgrading +#### Upgrade to v8 + +All standalone helper components were renamed to avoid import naming collision. + +- `` was renamed to ``. +- `` was renamed to ``. +- `` was renamed to ``. +- `` was renamed to `` was renamed to ``. + +> A [codemod](https://github.com/ghengeveld/react-async/tree/master/codemods) is available to automate the upgrade. + #### Upgrade to v6 - `` was renamed to ``. +- Some of the other helpers were also renamed, but the old ones remain as alias. - Don't forget to deal with any custom instances of `` when upgrading. +> A [codemod](https://github.com/ghengeveld/react-async/tree/master/codemods) is available to automate the upgrade. + #### Upgrade to v4 - `deferFn` now receives an `args` array as the first argument, instead of arguments to `run` being spread at the front diff --git a/codemods/README.md b/codemods/README.md new file mode 100644 index 00000000..074753ff --- /dev/null +++ b/codemods/README.md @@ -0,0 +1,33 @@ +# React Async codemods + +These codemods enable you to automatically upgrade your codebase to handle breaking changes in +React Async's API. + +## Warning + +Be aware: **codemods transform your source code in place**. Make sure that your files are in +version control before running a codemod. + +These codemods come without warranty. They will work fine most of the time, but you should always +verify their output. Also, **do not run a codemod more than once.** + +## Running a codemod + +These codemods are based on [jscodeshift](https://github.com/facebook/jscodeshift). Refer to their +docs for specifics. + +```bash +npx jscodeshift -t +``` + +Where `` should be replaced with the path to your project's source directory and +`` should be replaced by the URL of the codemod. + +For example: + +```bash +npx jscodeshift . -t https://raw.githubusercontent.com/ghengeveld/react-async/master/codemods/v6.js +``` + +This will apply the codemod for [v6](https://github.com/ghengeveld/react-async/blob/master/codemods/v6.js) +to the current working directory (`.`). diff --git a/codemods/v6.js b/codemods/v6.js new file mode 100644 index 00000000..b1562986 --- /dev/null +++ b/codemods/v6.js @@ -0,0 +1,54 @@ +/** + * This renames: + * - to + * - to + * - to + * + * This includes any custom instances created with createInstance(). + */ + +export default function transform({ path, source }, api) { + if (path.includes("/node_modules/")) return + + const j = api.jscodeshift + const root = j(source) + + const renameJsxMembers = parentName => { + root + .find(j.JSXMemberExpression, { object: { name: parentName }, property: { name: "Pending" } }) + .forEach(node => (node.value.property.name = "Initial")) + root + .find(j.JSXMemberExpression, { object: { name: parentName }, property: { name: "Loading" } }) + .forEach(node => (node.value.property.name = "Pending")) + root + .find(j.JSXMemberExpression, { object: { name: parentName }, property: { name: "Resolved" } }) + .forEach(node => (node.value.property.name = "Fulfilled")) + } + + // Rename instances using default import + root + .find(j.ImportDeclaration, { source: { value: "react-async" } }) + .find(j.ImportDefaultSpecifier) + .forEach(node => renameJsxMembers(node.value.local.name)) + + // Rename instances using named `Async` import + root + .find(j.ImportDeclaration, { source: { value: "react-async" } }) + .find(j.ImportSpecifier, { imported: { name: "Async" } }) + .forEach(node => renameJsxMembers(node.value.local.name)) + + // Rename instances created with `createInstance` + root + .find(j.ImportDeclaration, { source: { value: "react-async" } }) + .find(j.ImportSpecifier, { imported: { name: "createInstance" } }) + .forEach(node => { + const createInstance = node.value.local.name + root + .find(j.VariableDeclarator) + .filter(node => node.value.init.type === "CallExpression") + .filter(node => node.value.init.callee.name === createInstance) + .forEach(node => renameJsxMembers(node.value.id.name)) + }) + + return root.toSource() +} diff --git a/codemods/v8.js b/codemods/v8.js new file mode 100644 index 00000000..2ff6dc9f --- /dev/null +++ b/codemods/v8.js @@ -0,0 +1,33 @@ +/** + * This renames the standalone helper components: + * - to + * - to + * - to + * - to + * - to + */ + +const helperNames = ["Initial", "Pending", "Fulfilled", "Rejected", "Settled"] + +export default function transform({ path, source }, api) { + if (path.includes("/node_modules/")) return + + const j = api.jscodeshift + const root = j(source) + + // Rename imports + root + .find(j.ImportDeclaration, { source: { value: "react-async" } }) + .find(j.ImportSpecifier) + .filter(node => helperNames.includes(node.value.imported.name)) + .forEach(node => (node.value.imported.name = `If${node.value.imported.name}`)) + + // Rename JSX elements + root + .find(j.JSXIdentifier) + .filter(node => helperNames.includes(node.value.name)) + .filter(node => node.parentPath.value.type !== "JSXMemberExpression") + .forEach(node => (node.value.name = `If${node.value.name}`)) + + return root.toSource() +} From b2f78ba1cb570ab3726cc90b153e4c3e66729dca Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Thu, 8 Aug 2019 13:42:57 +0200 Subject: [PATCH 4/7] ES modules don't work from a codemod URL. --- codemods/v6.js | 2 +- codemods/v8.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codemods/v6.js b/codemods/v6.js index b1562986..9b2b14f7 100644 --- a/codemods/v6.js +++ b/codemods/v6.js @@ -7,7 +7,7 @@ * This includes any custom instances created with createInstance(). */ -export default function transform({ path, source }, api) { +module.exports = function transform({ path, source }, api) { if (path.includes("/node_modules/")) return const j = api.jscodeshift diff --git a/codemods/v8.js b/codemods/v8.js index 2ff6dc9f..53561a80 100644 --- a/codemods/v8.js +++ b/codemods/v8.js @@ -9,7 +9,7 @@ const helperNames = ["Initial", "Pending", "Fulfilled", "Rejected", "Settled"] -export default function transform({ path, source }, api) { +module.exports = function transform({ path, source }, api) { if (path.includes("/node_modules/")) return const j = api.jscodeshift From 5e8e426c149d9c9bc6dc67536e0a7cec39660ca2 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Thu, 8 Aug 2019 14:18:47 +0200 Subject: [PATCH 5/7] Ignore node_modules in cwd. --- codemods/v6.js | 2 +- codemods/v8.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codemods/v6.js b/codemods/v6.js index 9b2b14f7..59c22f56 100644 --- a/codemods/v6.js +++ b/codemods/v6.js @@ -8,7 +8,7 @@ */ module.exports = function transform({ path, source }, api) { - if (path.includes("/node_modules/")) return + if (path.includes("node_modules/")) return const j = api.jscodeshift const root = j(source) diff --git a/codemods/v8.js b/codemods/v8.js index 53561a80..b3aee040 100644 --- a/codemods/v8.js +++ b/codemods/v8.js @@ -10,7 +10,7 @@ const helperNames = ["Initial", "Pending", "Fulfilled", "Rejected", "Settled"] module.exports = function transform({ path, source }, api) { - if (path.includes("/node_modules/")) return + if (path.includes("node_modules/")) return const j = api.jscodeshift const root = j(source) From 68759e84171c3c2e4a22ee02b1d5be716db580b8 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Thu, 8 Aug 2019 16:36:36 +0200 Subject: [PATCH 6/7] Improve types for promiseFn/deferFn props. --- examples/with-typescript/src/App.tsx | 22 +++++++++++++++++----- packages/react-async/src/index.d.ts | 12 ++++++++---- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/examples/with-typescript/src/App.tsx b/examples/with-typescript/src/App.tsx index 16aa1a9b..4205a66d 100644 --- a/examples/with-typescript/src/App.tsx +++ b/examples/with-typescript/src/App.tsx @@ -1,13 +1,25 @@ import React, { Component } from "react" -import Async, { createInstance, useAsync, IfPending, IfRejected, IfFulfilled } from "react-async" +import Async, { + createInstance, + useAsync, + IfPending, + IfRejected, + IfFulfilled, + PromiseFn, +} from "react-async" import DevTools from "react-async-devtools" import "./App.css" -const promiseFn = () => Promise.resolve("baz") -const CustomAsync = createInstance({ promiseFn }) +const loadFirstName: PromiseFn = ({ userId }) => + fetch(`https://reqres.in/api/users/${userId}`) + .then(res => (res.ok ? Promise.resolve(res) : Promise.reject(res))) + .then(res => res.json()) + .then(({ data }) => data.first_name) + +const CustomAsync = createInstance({ promiseFn: loadFirstName }) const UseAsync = () => { - const state = useAsync({ promiseFn }) + const state = useAsync({ promiseFn: loadFirstName, userId: 1 }) return ( <> Loading... @@ -34,7 +46,7 @@ class App extends Component { Promise.resolve("bar")}> {data => <>{data}} - + {data => <>{data}} diff --git a/packages/react-async/src/index.d.ts b/packages/react-async/src/index.d.ts index c9215bcb..6db286d7 100644 --- a/packages/react-async/src/index.d.ts +++ b/packages/react-async/src/index.d.ts @@ -13,8 +13,12 @@ export type SettledChildren = | ((state: AsyncFulfilled | AsyncRejected) => React.ReactNode) | React.ReactNode -export type PromiseFn = (props: object, controller: AbortController) => Promise -export type DeferFn = (args: any[], props: object, controller: AbortController) => Promise +export type PromiseFn = (props: AsyncProps, controller: AbortController) => Promise +export type DeferFn = ( + args: any[], + props: AsyncProps, + controller: AbortController +) => Promise interface AbstractAction { type: string @@ -31,7 +35,7 @@ export interface AsyncOptions { promiseFn?: PromiseFn deferFn?: DeferFn watch?: any - watchFn?: (props: object, prevProps: object) => any + watchFn?: (props: AsyncProps, prevProps: AsyncProps) => any initialValue?: T onResolve?: (data: T) => void onReject?: (error: Error) => void @@ -43,7 +47,7 @@ export interface AsyncOptions { dispatcher?: ( action: AsyncAction, internalDispatch: (action: AsyncAction) => void, - props: object + props: AsyncProps ) => void debugLabel?: string [prop: string]: any From 8f61a922667dfbb76d5714914f732ff724eac136 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Thu, 8 Aug 2019 21:30:15 +0200 Subject: [PATCH 7/7] Bump and align all package versions. --- examples/basic-fetch/package.json | 6 +++--- examples/basic-hook/package.json | 6 +++--- examples/custom-instance/package.json | 6 +++--- examples/movie-app/package.json | 6 +++--- examples/with-abortcontroller/package.json | 6 +++--- examples/with-nextjs/package.json | 6 +++--- examples/with-react-native/package.json | 4 ++-- examples/with-react-router/package.json | 6 +++--- examples/with-typescript/package.json | 6 +++--- lerna.json | 2 +- packages/react-async-devtools/package.json | 2 +- packages/react-async/package.json | 2 +- 12 files changed, 29 insertions(+), 29 deletions(-) diff --git a/examples/basic-fetch/package.json b/examples/basic-fetch/package.json index b2c293f1..3be8acab 100644 --- a/examples/basic-fetch/package.json +++ b/examples/basic-fetch/package.json @@ -1,6 +1,6 @@ { "name": "basic-fetch-example", - "version": "1.0.2", + "version": "8.0.0-alpha.0", "private": true, "homepage": "https://react-async.ghengeveld.now.sh/examples/basic-fetch", "scripts": { @@ -15,8 +15,8 @@ }, "dependencies": { "react": "^16.8.6", - "react-async": "^7.0.6", - "react-async-devtools": "^1.0.4", + "react-async": "^8.0.0-alpha.0", + "react-async-devtools": "^8.0.0-alpha.0", "react-dom": "^16.8.6", "react-scripts": "^3.0.1" }, diff --git a/examples/basic-hook/package.json b/examples/basic-hook/package.json index 160fa59c..ee22374c 100644 --- a/examples/basic-hook/package.json +++ b/examples/basic-hook/package.json @@ -1,6 +1,6 @@ { "name": "basic-hook-example", - "version": "1.0.3", + "version": "8.0.0-alpha.0", "private": true, "homepage": "https://react-async.ghengeveld.now.sh/examples/basic-hook", "scripts": { @@ -15,8 +15,8 @@ }, "dependencies": { "react": "^16.8.6", - "react-async": "^7.0.6", - "react-async-devtools": "^1.0.4", + "react-async": "^8.0.0-alpha.0", + "react-async-devtools": "^8.0.0-alpha.0", "react-dom": "^16.8.6", "react-scripts": "^3.0.1" }, diff --git a/examples/custom-instance/package.json b/examples/custom-instance/package.json index 3d214e96..e2f44d66 100644 --- a/examples/custom-instance/package.json +++ b/examples/custom-instance/package.json @@ -1,6 +1,6 @@ { "name": "custom-instance-example", - "version": "1.0.2", + "version": "8.0.0-alpha.0", "private": true, "homepage": "https://react-async.ghengeveld.now.sh/examples/custom-instance", "scripts": { @@ -15,8 +15,8 @@ }, "dependencies": { "react": "^16.8.6", - "react-async": "^7.0.6", - "react-async-devtools": "^1.0.4", + "react-async": "^8.0.0-alpha.0", + "react-async-devtools": "^8.0.0-alpha.0", "react-dom": "^16.8.6", "react-scripts": "^3.0.1" }, diff --git a/examples/movie-app/package.json b/examples/movie-app/package.json index 54a2f06b..a2e3ae4d 100644 --- a/examples/movie-app/package.json +++ b/examples/movie-app/package.json @@ -1,6 +1,6 @@ { "name": "movie-app-example", - "version": "1.0.3", + "version": "8.0.0-alpha.0", "private": true, "homepage": "https://react-async.ghengeveld.now.sh/examples/movie-app", "scripts": { @@ -15,8 +15,8 @@ }, "dependencies": { "react": "^16.8.6", - "react-async": "^7.0.6", - "react-async-devtools": "^1.0.4", + "react-async": "^8.0.0-alpha.0", + "react-async-devtools": "^8.0.0-alpha.0", "react-dom": "^16.8.6", "react-scripts": "^3.0.1" }, diff --git a/examples/with-abortcontroller/package.json b/examples/with-abortcontroller/package.json index f85e99de..8c564481 100644 --- a/examples/with-abortcontroller/package.json +++ b/examples/with-abortcontroller/package.json @@ -1,6 +1,6 @@ { "name": "with-abortcontroller-example", - "version": "1.0.2", + "version": "8.0.0-alpha.0", "private": true, "homepage": "https://react-async.ghengeveld.now.sh/examples/with-abortcontroller", "scripts": { @@ -15,8 +15,8 @@ }, "dependencies": { "react": "^16.8.6", - "react-async": "^7.0.6", - "react-async-devtools": "^1.0.4", + "react-async": "^8.0.0-alpha.0", + "react-async-devtools": "^8.0.0-alpha.0", "react-dom": "^16.8.6", "react-scripts": "^3.0.1" }, diff --git a/examples/with-nextjs/package.json b/examples/with-nextjs/package.json index 9625e081..cc0bf7cf 100644 --- a/examples/with-nextjs/package.json +++ b/examples/with-nextjs/package.json @@ -1,6 +1,6 @@ { "name": "with-nextjs-example", - "version": "1.0.4", + "version": "8.0.0-alpha.0", "private": true, "main": "index.js", "scripts": { @@ -18,8 +18,8 @@ "isomorphic-fetch": "^2.2.1", "next": "^9.0.2", "react": "^16.8.6", - "react-async": "^7.0.6", - "react-async-devtools": "^1.0.4", + "react-async": "^8.0.0-alpha.0", + "react-async-devtools": "^8.0.0-alpha.0", "react-dom": "^16.8.6" }, "devDependencies": { diff --git a/examples/with-react-native/package.json b/examples/with-react-native/package.json index eb622b38..87505762 100644 --- a/examples/with-react-native/package.json +++ b/examples/with-react-native/package.json @@ -1,6 +1,6 @@ { "name": "with-react-native-example", - "version": "0.0.1", + "version": "8.0.0-alpha.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "postinstall": "relative-deps", @@ -16,7 +16,7 @@ "dependencies": { "expo": "^33.0.0", "react": "16.8.3", - "react-async": "^7.0.6", + "react-async": "^8.0.0-alpha.0", "react-dom": "^16.8.6", "react-native": "https://github.com/expo/react-native/archive/sdk-33.0.0.tar.gz", "react-native-web": "^0.11.4" diff --git a/examples/with-react-router/package.json b/examples/with-react-router/package.json index 169d5932..b3c5c977 100644 --- a/examples/with-react-router/package.json +++ b/examples/with-react-router/package.json @@ -1,6 +1,6 @@ { "name": "with-react-router-example", - "version": "1.0.3", + "version": "8.0.0-alpha.0", "private": true, "main": "index.js", "scripts": { @@ -12,8 +12,8 @@ }, "dependencies": { "react": "^16.8.6", - "react-async": "^7.0.6", - "react-async-devtools": "^1.0.4", + "react-async": "^8.0.0-alpha.0", + "react-async-devtools": "^8.0.0-alpha.0", "react-dom": "^16.8.6", "react-router-dom": "^5.0.0" }, diff --git a/examples/with-typescript/package.json b/examples/with-typescript/package.json index 4fe84435..1c274ee7 100644 --- a/examples/with-typescript/package.json +++ b/examples/with-typescript/package.json @@ -1,6 +1,6 @@ { "name": "with-typescript-example", - "version": "1.0.3", + "version": "8.0.0-alpha.0", "private": true, "homepage": "https://react-async.ghengeveld.now.sh/examples/with-typescript", "scripts": { @@ -18,8 +18,8 @@ "@types/react": "^16.8.19", "@types/react-dom": "^16.8.4", "react": "^16.8.6", - "react-async": "^7.0.6", - "react-async-devtools": "^1.0.4", + "react-async": "^8.0.0-alpha.0", + "react-async-devtools": "^8.0.0-alpha.0", "react-dom": "^16.8.6", "react-scripts": "^3.0.1", "typescript": "^3.5.1" diff --git a/lerna.json b/lerna.json index 8b038c63..bbc5aab8 100644 --- a/lerna.json +++ b/lerna.json @@ -5,5 +5,5 @@ "ignoreChanges": ["examples/**"] }, "useWorkspaces": true, - "version": "independent" + "version": "8.0.0-alpha.0" } diff --git a/packages/react-async-devtools/package.json b/packages/react-async-devtools/package.json index c8146a4f..17e39b50 100644 --- a/packages/react-async-devtools/package.json +++ b/packages/react-async-devtools/package.json @@ -1,6 +1,6 @@ { "name": "react-async-devtools", - "version": "1.0.4", + "version": "8.0.0-alpha.0", "description": "DevTools for React Async", "keywords": [ "react", diff --git a/packages/react-async/package.json b/packages/react-async/package.json index 4a17c761..9fd93bfd 100644 --- a/packages/react-async/package.json +++ b/packages/react-async/package.json @@ -1,6 +1,6 @@ { "name": "react-async", - "version": "7.0.6", + "version": "8.0.0-alpha.0", "description": "React component for declarative promise resolution and data fetching", "keywords": [ "react",