From 2595e01323a35d364bd5258aa1439dd3e0528995 Mon Sep 17 00:00:00 2001 From: Markus Ylisiurunen <8409947+markusylisiurunen@users.noreply.github.com> Date: Sun, 4 Aug 2019 15:18:44 +0300 Subject: [PATCH 1/5] Add new prop to Async to skip initial fetch on mount --- packages/react-async/src/Async.js | 8 +++-- packages/react-async/src/Async.spec.js | 40 +++++++++++++++++++++++++ packages/react-async/src/index.d.ts | 1 + packages/react-async/src/propTypes.js | 1 + packages/react-async/src/reducer.js | 5 ++-- packages/react-async/src/status.js | 9 +++--- packages/react-async/src/status.spec.js | 14 +++++++-- 7 files changed, 67 insertions(+), 11 deletions(-) diff --git a/packages/react-async/src/Async.js b/packages/react-async/src/Async.js index 45c17dfe..c69dbb52 100644 --- a/packages/react-async/src/Async.js +++ b/packages/react-async/src/Async.js @@ -28,13 +28,17 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { const promise = props.promise const promiseFn = props.promiseFn || defaultProps.promiseFn const initialValue = props.initialValue || defaultProps.initialValue + let skipOnMount = false + + if (defaultProps.skipOnMount !== undefined) skipOnMount = defaultProps.skipOnMount + if (props.skipOnMount !== undefined) skipOnMount = props.skipOnMount this.mounted = false this.counter = 0 this.args = [] this.abortController = { abort: () => {} } this.state = { - ...init({ initialValue, promise, promiseFn }), + ...init({ initialValue, skipOnMount, promise, promiseFn }), cancel: this.cancel, run: this.run, reload: () => { @@ -60,7 +64,7 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { componentDidMount() { this.mounted = true - if (this.props.promise || !this.state.initialValue) { + if (this.props.promise || (!this.state.skipOnMount && !this.state.initialValue)) { this.load() } } diff --git a/packages/react-async/src/Async.spec.js b/packages/react-async/src/Async.spec.js index 3a4672c4..20adda05 100644 --- a/packages/react-async/src/Async.spec.js +++ b/packages/react-async/src/Async.spec.js @@ -53,6 +53,46 @@ describe("Async", () => { ) expect(one).toBe(two) }) + + test("skips the initial fetch on mount and fetches only after fetch is re-requested", async () => { + const promiseFn = () => resolveTo("data") + const { rerender, getByText, queryByText } = render( + + {({ initialValue }) =>
{initialValue}
}
+ {value =>
{value}
}
+
+ ) + + expect(queryByText("initial")).toBeInTheDocument() + expect(queryByText("data")).toBeNull() + + rerender( + + {({ initialValue }) =>
{initialValue}
}
+ {value =>
{value}
}
+
+ ) + + await waitForElement(() => getByText("data")) + + expect(queryByText("initial")).toBeNull() + expect(queryByText("data")).toBeInTheDocument() + }) + + test("does not skip the initial fetch if promise is given", async () => { + const promise = resolveTo("data") + const { getByText, queryByText } = render( + + {({ initialValue }) =>
{initialValue}
}
+ {value =>
{value}
}
+
+ ) + + await waitForElement(() => getByText("data")) + + expect(queryByText("initial")).toBeNull() + expect(queryByText("data")).toBeInTheDocument() + }) }) describe("Async.Fulfilled", () => { diff --git a/packages/react-async/src/index.d.ts b/packages/react-async/src/index.d.ts index 5a930e18..ce117a32 100644 --- a/packages/react-async/src/index.d.ts +++ b/packages/react-async/src/index.d.ts @@ -21,6 +21,7 @@ export interface AsyncOptions { watch?: any watchFn?: (props: object, prevProps: object) => any initialValue?: T + skipOnMount?: boolean onResolve?: (data: T) => void onReject?: (error: Error) => void reducer?: ( diff --git a/packages/react-async/src/propTypes.js b/packages/react-async/src/propTypes.js index ff8e3964..831e35e7 100644 --- a/packages/react-async/src/propTypes.js +++ b/packages/react-async/src/propTypes.js @@ -38,6 +38,7 @@ export default PropTypes && { watch: PropTypes.any, watchFn: PropTypes.func, initialValue: PropTypes.any, + skipOnMount: PropTypes.bool, onResolve: PropTypes.func, onReject: PropTypes.func, reducer: PropTypes.func, diff --git a/packages/react-async/src/reducer.js b/packages/react-async/src/reducer.js index df22f5d7..51db6de9 100644 --- a/packages/react-async/src/reducer.js +++ b/packages/react-async/src/reducer.js @@ -7,14 +7,15 @@ export const actionTypes = { reject: "reject", } -export const init = ({ initialValue, promise, promiseFn }) => ({ +export const init = ({ initialValue, skipOnMount, promise, promiseFn }) => ({ initialValue, + skipOnMount, data: initialValue instanceof Error ? undefined : initialValue, error: initialValue instanceof Error ? initialValue : undefined, value: initialValue, startedAt: promise || promiseFn ? new Date() : undefined, finishedAt: initialValue ? new Date() : undefined, - ...getStatusProps(getInitialStatus(initialValue, promise || promiseFn)), + ...getStatusProps(getInitialStatus(initialValue, skipOnMount, promise || promiseFn)), counter: 0, }) diff --git a/packages/react-async/src/status.js b/packages/react-async/src/status.js index 0af8fd52..5b7a0815 100644 --- a/packages/react-async/src/status.js +++ b/packages/react-async/src/status.js @@ -5,10 +5,11 @@ export const statusTypes = { rejected: "rejected", } -export const getInitialStatus = (value, promise) => { - if (value instanceof Error) return statusTypes.rejected - if (value !== undefined) return statusTypes.fulfilled - if (promise) return statusTypes.pending +export const getInitialStatus = (initialValue, skipOnMount, promise) => { + if (initialValue instanceof Error) return statusTypes.rejected + if (initialValue !== undefined && !skipOnMount) return statusTypes.fulfilled + if (promise instanceof Promise) return statusTypes.pending + if (typeof promise === "function" && !skipOnMount) return statusTypes.pending return statusTypes.initial } diff --git a/packages/react-async/src/status.spec.js b/packages/react-async/src/status.spec.js index 5f2fa580..6b03e715 100644 --- a/packages/react-async/src/status.spec.js +++ b/packages/react-async/src/status.spec.js @@ -6,16 +6,24 @@ import { getInitialStatus, getIdleStatus, statusTypes } from "./status" describe("getInitialStatus", () => { test("returns 'initial' when given an undefined value", () => { - expect(getInitialStatus(undefined)).toEqual(statusTypes.initial) + expect(getInitialStatus(undefined, false, undefined)).toEqual(statusTypes.initial) + }) + test("returns 'initial' when requested to skip on mount", () => { + expect(getInitialStatus("initial", true, () => Promise.resolve("foo"))).toEqual( + statusTypes.initial + ) + }) + test("returns 'pending' when requested to skip on mount but given a promise", () => { + expect(getInitialStatus("initial", true, Promise.resolve("foo"))).toEqual(statusTypes.pending) }) test("returns 'pending' when given only a promise", () => { - expect(getInitialStatus(undefined, Promise.resolve("foo"))).toEqual(statusTypes.pending) + expect(getInitialStatus(undefined, false, Promise.resolve("foo"))).toEqual(statusTypes.pending) }) test("returns 'rejected' when given an Error value", () => { expect(getInitialStatus(new Error("oops"))).toEqual(statusTypes.rejected) }) test("returns 'fulfilled' when given any other value", () => { - expect(getInitialStatus(null)).toEqual(statusTypes.fulfilled) + expect(getInitialStatus(null, false)).toEqual(statusTypes.fulfilled) }) }) From 89268ef514977044866a720ca8985b4240984ad6 Mon Sep 17 00:00:00 2001 From: Markus Ylisiurunen <8409947+markusylisiurunen@users.noreply.github.com> Date: Sun, 4 Aug 2019 16:30:13 +0300 Subject: [PATCH 2/5] Add the same property to useAsync as well --- packages/react-async/src/useAsync.js | 17 +++++++++++----- packages/react-async/src/useAsync.spec.js | 24 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/packages/react-async/src/useAsync.js b/packages/react-async/src/useAsync.js index b259d98a..c511abe4 100644 --- a/packages/react-async/src/useAsync.js +++ b/packages/react-async/src/useAsync.js @@ -62,7 +62,7 @@ const useAsync = (arg1, arg2) => { }) } - const { promise, promiseFn, initialValue } = options + const { promise, promiseFn } = options const load = () => { if (promise) { return start(() => promise).then( @@ -70,8 +70,7 @@ const useAsync = (arg1, arg2) => { handleReject(counter.current) ) } - const isPreInitialized = initialValue && counter.current === 0 - if (promiseFn && !isPreInitialized) { + if (promiseFn) { return start(() => promiseFn(options, abortController.current)).then( handleResolve(counter.current), handleReject(counter.current) @@ -97,13 +96,21 @@ const useAsync = (arg1, arg2) => { isMounted.current && dispatch({ type: actionTypes.cancel, meta: getMeta() }) } - const { watch, watchFn } = options + const { watch, watchFn, initialValue, skipOnMount = false } = options useEffect(() => { if (watchFn && prevOptions.current && watchFn(options, prevOptions.current)) load() }) useEffect(() => { if (counter.current) cancel() - if (promise || promiseFn) load() + if (promise) load() + // promiseFn is given AND is not onMount with initialValue AND is not onMount with skipOnMount + // => use promiseFn on mount if not initialValue or skipOnMount AND if not on mount, always use promiseFn + else if ( + promiseFn && + !(initialValue && !prevOptions.current) && + !(skipOnMount && !prevOptions.current) + ) + load() }, [promise, promiseFn, watch]) useEffect(() => () => (isMounted.current = false), []) useEffect(() => () => cancel(), []) diff --git a/packages/react-async/src/useAsync.spec.js b/packages/react-async/src/useAsync.spec.js index 3bbba134..6bd52f28 100644 --- a/packages/react-async/src/useAsync.spec.js +++ b/packages/react-async/src/useAsync.spec.js @@ -84,6 +84,30 @@ describe("useAsync", () => { await sleep(10) // resolve deferFn expect(promiseFn).toHaveBeenLastCalledWith(expect.objectContaining({ count: 1 }), abortCtrl) }) + + test("skips the initial fetch if skipOnMount passed", async () => { + const promiseFn = () => resolveTo("foo") + const Component = ({ bar }) => { + const foo = useAsync({ promiseFn, watch: bar, skipOnMount: true }) + return ( +
+ {bar} {foo.data ? foo.data : "undefined"} +
+ ) + } + + const { rerender, getByText, queryByText } = render() + + await waitForElement(() => getByText("1 undefined")) + expect(queryByText("1 undefined")).toBeInTheDocument() + + rerender() + + await waitForElement(() => getByText("2 foo")) + + expect(queryByText("2 foo")).toBeInTheDocument() + expect(queryByText("1 undefined")).toBeNull() + }) }) describe("useFetch", () => { From 5da83ab996ad783a1561ee156a50df7bd0428c87 Mon Sep 17 00:00:00 2001 From: Markus Ylisiurunen <8409947+markusylisiurunen@users.noreply.github.com> Date: Sun, 4 Aug 2019 17:17:37 +0300 Subject: [PATCH 3/5] Add example for debounced search --- examples/debounced-search/.env | 1 + examples/debounced-search/README.md | 7 ++ examples/debounced-search/package.json | 41 ++++++++++ examples/debounced-search/public/favicon.ico | Bin 0 -> 3870 bytes examples/debounced-search/public/index.html | 13 +++ examples/debounced-search/src/index.css | 6 ++ examples/debounced-search/src/index.js | 81 +++++++++++++++++++ examples/debounced-search/src/index.test.js | 9 +++ 8 files changed, 158 insertions(+) create mode 100644 examples/debounced-search/.env create mode 100644 examples/debounced-search/README.md create mode 100644 examples/debounced-search/package.json create mode 100644 examples/debounced-search/public/favicon.ico create mode 100644 examples/debounced-search/public/index.html create mode 100644 examples/debounced-search/src/index.css create mode 100644 examples/debounced-search/src/index.js create mode 100644 examples/debounced-search/src/index.test.js diff --git a/examples/debounced-search/.env b/examples/debounced-search/.env new file mode 100644 index 00000000..7d910f14 --- /dev/null +++ b/examples/debounced-search/.env @@ -0,0 +1 @@ +SKIP_PREFLIGHT_CHECK=true \ No newline at end of file diff --git a/examples/debounced-search/README.md b/examples/debounced-search/README.md new file mode 100644 index 00000000..f2c57fa2 --- /dev/null +++ b/examples/debounced-search/README.md @@ -0,0 +1,7 @@ +# Debounced search with useAsync hook + +This demonstrates how to use the `useAsync` hook to do debounced search. + + + live demo + diff --git a/examples/debounced-search/package.json b/examples/debounced-search/package.json new file mode 100644 index 00000000..bd65a571 --- /dev/null +++ b/examples/debounced-search/package.json @@ -0,0 +1,41 @@ +{ + "name": "debounced-search-example", + "version": "1.0.0", + "private": true, + "homepage": "https://react-async.ghengeveld.now.sh/examples/debounced-search-example", + "scripts": { + "postinstall": "relative-deps", + "prestart": "relative-deps", + "prebuild": "relative-deps", + "pretest": "relative-deps", + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "now-build": "SKIP_PREFLIGHT_CHECK=true react-scripts build" + }, + "dependencies": { + "debounce": "^1.2.0", + "faker": "^4.1.0", + "react": "^16.8.6", + "react-async": "^7.0.5", + "react-async-devtools": "^1.0.3", + "react-dom": "^16.8.6", + "react-scripts": "^3.0.1" + }, + "devDependencies": { + "relative-deps": "^0.1.2" + }, + "relativeDependencies": { + "react-async": "../../packages/react-async/pkg", + "react-async-devtools": "../../packages/react-async-devtools/pkg" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} diff --git a/examples/debounced-search/public/favicon.ico b/examples/debounced-search/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 zcma);c{J4h9>;%nil|2-o+rCuEF-(I%-F}ijC~o(k~HKAkr0)!FCj~d>`RtpD?8b; zXOC1OD!V*IsqUwzbMF1)-gEDD=A573Z-&G7^LoAC9|WO7Xc0Cx1g^Zu0u_SjAPB3vGa^W|sj)80f#V0@M_CAZTIO(t--xg= z!sii`1giyH7EKL_+Wi0ab<)&E_0KD!3Rp2^HNB*K2@PHCs4PWSA32*-^7d{9nH2_E zmC{C*N*)(vEF1_aMamw2A{ZH5aIDqiabnFdJ|y0%aS|64E$`s2ccV~3lR!u<){eS` z#^Mx6o(iP1Ix%4dv`t@!&Za-K@mTm#vadc{0aWDV*_%EiGK7qMC_(`exc>-$Gb9~W!w_^{*pYRm~G zBN{nA;cm^w$VWg1O^^<6vY`1XCD|s_zv*g*5&V#wv&s#h$xlUilPe4U@I&UXZbL z0)%9Uj&@yd03n;!7do+bfixH^FeZ-Ema}s;DQX2gY+7g0s(9;`8GyvPY1*vxiF&|w z>!vA~GA<~JUqH}d;DfBSi^IT*#lrzXl$fNpq0_T1tA+`A$1?(gLb?e#0>UELvljtQ zK+*74m0jn&)5yk8mLBv;=@}c{t0ztT<v;Avck$S6D`Z)^c0(jiwKhQsn|LDRY&w(Fmi91I7H6S;b0XM{e zXp0~(T@k_r-!jkLwd1_Vre^v$G4|kh4}=Gi?$AaJ)3I+^m|Zyj#*?Kp@w(lQdJZf4 z#|IJW5z+S^e9@(6hW6N~{pj8|NO*>1)E=%?nNUAkmv~OY&ZV;m-%?pQ_11)hAr0oAwILrlsGawpxx4D43J&K=n+p3WLnlDsQ$b(9+4 z?mO^hmV^F8MV{4Lx>(Q=aHhQ1){0d*(e&s%G=i5rq3;t{JC zmgbn5Nkl)t@fPH$v;af26lyhH!k+#}_&aBK4baYPbZy$5aFx4}ka&qxl z$=Rh$W;U)>-=S-0=?7FH9dUAd2(q#4TCAHky!$^~;Dz^j|8_wuKc*YzfdAht@Q&ror?91Dm!N03=4=O!a)I*0q~p0g$Fm$pmr$ zb;wD;STDIi$@M%y1>p&_>%?UP($15gou_ue1u0!4(%81;qcIW8NyxFEvXpiJ|H4wz z*mFT(qVx1FKufG11hByuX%lPk4t#WZ{>8ka2efjY`~;AL6vWyQKpJun2nRiZYDij$ zP>4jQXPaP$UC$yIVgGa)jDV;F0l^n(V=HMRB5)20V7&r$jmk{UUIe zVjKroK}JAbD>B`2cwNQ&GDLx8{pg`7hbA~grk|W6LgiZ`8y`{Iq0i>t!3p2}MS6S+ zO_ruKyAElt)rdS>CtF7j{&6rP-#c=7evGMt7B6`7HG|-(WL`bDUAjyn+k$mx$CH;q2Dz4x;cPP$hW=`pFfLO)!jaCL@V2+F)So3}vg|%O*^T1j>C2lx zsURO-zIJC$^$g2byVbRIo^w>UxK}74^TqUiRR#7s_X$e)$6iYG1(PcW7un-va-S&u zHk9-6Zn&>T==A)lM^D~bk{&rFzCi35>UR!ZjQkdSiNX*-;l4z9j*7|q`TBl~Au`5& z+c)*8?#-tgUR$Zd%Q3bs96w6k7q@#tUn`5rj+r@_sAVVLqco|6O{ILX&U-&-cbVa3 zY?ngHR@%l{;`ri%H*0EhBWrGjv!LE4db?HEWb5mu*t@{kv|XwK8?npOshmzf=vZA@ zVSN9sL~!sn?r(AK)Q7Jk2(|M67Uy3I{eRy z_l&Y@A>;vjkWN5I2xvFFTLX0i+`{qz7C_@bo`ZUzDugfq4+>a3?1v%)O+YTd6@Ul7 zAfLfm=nhZ`)P~&v90$&UcF+yXm9sq!qCx3^9gzIcO|Y(js^Fj)Rvq>nQAHI92ap=P z10A4@prk+AGWCb`2)dQYFuR$|H6iDE8p}9a?#nV2}LBCoCf(Xi2@szia7#gY>b|l!-U`c}@ zLdhvQjc!BdLJvYvzzzngnw51yRYCqh4}$oRCy-z|v3Hc*d|?^Wj=l~18*E~*cR_kU z{XsxM1i{V*4GujHQ3DBpl2w4FgFR48Nma@HPgnyKoIEY-MqmMeY=I<%oG~l!f<+FN z1ZY^;10j4M4#HYXP zw5eJpA_y(>uLQ~OucgxDLuf}fVs272FaMxhn4xnDGIyLXnw>Xsd^J8XhcWIwIoQ9} z%FoSJTAGW(SRGwJwb=@pY7r$uQRK3Zd~XbxU)ts!4XsJrCycrWSI?e!IqwqIR8+Jh zlRjZ`UO1I!BtJR_2~7AbkbSm%XQqxEPkz6BTGWx8e}nQ=w7bZ|eVP4?*Tb!$(R)iC z9)&%bS*u(lXqzitAN)Oo=&Ytn>%Hzjc<5liuPi>zC_nw;Z0AE3Y$Jao_Q90R-gl~5 z_xAb2J%eArrC1CN4G$}-zVvCqF1;H;abAu6G*+PDHSYFx@Tdbfox*uEd3}BUyYY-l zTfEsOqsi#f9^FoLO;ChK<554qkri&Av~SIM*{fEYRE?vH7pTAOmu2pz3X?Wn*!ROX ztd54huAk&mFBemMooL33RV-*1f0Q3_(7hl$<#*|WF9P!;r;4_+X~k~uKEqdzZ$5Al zV63XN@)j$FN#cCD;ek1R#l zv%pGrhB~KWgoCj%GT?%{@@o(AJGt*PG#l3i>lhmb_twKH^EYvacVY-6bsCl5*^~L0 zonm@lk2UvvTKr2RS%}T>^~EYqdL1q4nD%0n&Xqr^cK^`J5W;lRRB^R-O8b&HENO||mo0xaD+S=I8RTlIfVgqN@SXDr2&-)we--K7w= zJVU8?Z+7k9dy;s;^gDkQa`0nz6N{T?(A&Iz)2!DEecLyRa&FI!id#5Z7B*O2=PsR0 zEvc|8{NS^)!d)MDX(97Xw}m&kEO@5jqRaDZ!+%`wYOI<23q|&js`&o4xvjP7D_xv@ z5hEwpsp{HezI9!~6O{~)lLR@oF7?J7i>1|5a~UuoN=q&6N}EJPV_GD`&M*v8Y`^2j zKII*d_@Fi$+i*YEW+Hbzn{iQk~yP z>7N{S4)r*!NwQ`(qcN#8SRQsNK6>{)X12nbF`*7#ecO7I)Q$uZsV+xS4E7aUn+U(K baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/examples/debounced-search/public/index.html b/examples/debounced-search/public/index.html new file mode 100644 index 00000000..b8317902 --- /dev/null +++ b/examples/debounced-search/public/index.html @@ -0,0 +1,13 @@ + + + + + + + React App + + + +
+ + diff --git a/examples/debounced-search/src/index.css b/examples/debounced-search/src/index.css new file mode 100644 index 00000000..336fe6ba --- /dev/null +++ b/examples/debounced-search/src/index.css @@ -0,0 +1,6 @@ +body { + margin: 20px; + padding: 0px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/examples/debounced-search/src/index.js b/examples/debounced-search/src/index.js new file mode 100644 index 00000000..b4b6ff82 --- /dev/null +++ b/examples/debounced-search/src/index.js @@ -0,0 +1,81 @@ +import React, { useState, useEffect, useMemo } from "react" +import { useAsync } from "react-async" +import ReactDOM from "react-dom" +import faker from "faker" +import debounce from "debounce" +import DevTools from "react-async-devtools" + +import "./index.css" + +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +async function loadSearchResults() { + await sleep(500) + + const result = [] + + for (let i = 0; i < 10; i++) { + result.push(faker.name.findName()) + } + + return result +} + +function SearchResults({ searchTerm }) { + const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(searchTerm) + const results = useAsync({ + promiseFn: loadSearchResults, + watch: debouncedSearchTerm, + skipOnMount: true, + }) + + const debouncedUpdate = useMemo( + () => + debounce(nextSearchTerm => { + setDebouncedSearchTerm(nextSearchTerm) + }, 300), + [] + ) + + useEffect(() => { + debouncedUpdate(searchTerm) + return () => debouncedUpdate.clear() + }, [searchTerm, debouncedUpdate]) + + if (results.isPending || !results.data) return

Loading...

+ + return ( +
    + {results.data.map(result => ( +
  • {result}
  • + ))} +
+ ) +} + +function Search() { + const [searchTerm, setSearchTerm] = useState("") + + return ( + <> + setSearchTerm(e.target.value)} + /> + {searchTerm.length > 0 ? :

Main view

} + + ) +} + +export const App = () => ( + <> + + + +) + +if (process.env.NODE_ENV !== "test") ReactDOM.render(, document.getElementById("root")) diff --git a/examples/debounced-search/src/index.test.js b/examples/debounced-search/src/index.test.js new file mode 100644 index 00000000..2920612e --- /dev/null +++ b/examples/debounced-search/src/index.test.js @@ -0,0 +1,9 @@ +import React from "react" +import ReactDOM from "react-dom" +import { App } from "./" + +it("renders without crashing", () => { + const div = document.createElement("div") + ReactDOM.render(, div) + ReactDOM.unmountComponentAtNode(div) +}) From 9bc3a0d32b281b0c485cb2083da2fc254cccea0d Mon Sep 17 00:00:00 2001 From: Markus Ylisiurunen <8409947+markusylisiurunen@users.noreply.github.com> Date: Sun, 4 Aug 2019 17:19:39 +0300 Subject: [PATCH 4/5] Add documentation for the skipOnMount prop --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 23a232ae..b9a71efc 100644 --- a/README.md +++ b/README.md @@ -333,6 +333,7 @@ These can be passed in an object to `useAsync()`, or as props to `` and c - `watch` Watch a value and automatically reload when it changes. - `watchFn` Watch this function and automatically reload when it returns truthy. - `initialValue` Provide initial data or error for server-side rendering. +- `skipOnMount` Skip running `promiseFn` on mount, useful for debounced requests. - `onResolve` Callback invoked when Promise resolves. - `onReject` Callback invoked when Promise rejects. - `onCancel` Callback invoked when a Promise is cancelled. @@ -400,6 +401,13 @@ Re-runs the `promiseFn` when this callback returns truthy (called on every updat Initial state for `data` or `error` (if instance of Error); useful for server-side rendering. +#### `skipOnMount` + +> `boolean` + +Skips the `promiseFn` on mount and runs it only after refresh is requested in one way or another. +Useful for debounced actions to skip the initial fetch. + #### `onResolve` > `function(data: any): void` From e84ba05997d0759de2e1da2728a9d7cfd56d3eff Mon Sep 17 00:00:00 2001 From: Markus Ylisiurunen <8409947+markusylisiurunen@users.noreply.github.com> Date: Sun, 4 Aug 2019 17:20:57 +0300 Subject: [PATCH 5/5] Add new packages for the debounced-search example --- yarn.lock | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yarn.lock b/yarn.lock index b3556135..98f51197 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8105,6 +8105,11 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= +faker@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/faker/-/faker-4.1.0.tgz#1e45bbbecc6774b3c195fad2835109c6d748cc3f" + integrity sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8= + falafel@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.1.0.tgz#96bb17761daba94f46d001738b3cedf3a67fe06c"