diff --git a/README.md b/README.md
index 9ea66e2a..f4a87f32 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@ As an abstraction, this tool allows for greater consistency and maintainability
- [Loading and Error States](#loading-and-error-states)
- [Lazy Fetching](#lazy-fetching)
- [Response Resolution](#response-resolution)
+ - [Debouncing Requests](#debouncing-requests)
- [TypeScript Integration](#typescript-integration)
- [Mutations with `Mutate`](#mutations-with-mutate)
- [`Mutate` Component API](#mutate-component-api)
@@ -193,7 +194,10 @@ const MyAnimalsList = props => (
"OH NO!"
) : (
<>
-
Here are all my {props.animal}s!
+
+ Here are all my {props.animal}
+ s!
+
{animals.map(animal => - {animal}
)}
>
)}
@@ -215,7 +219,10 @@ const MyAnimalsList = props => (
) : (
You should only see this after things are loaded.
-
Here are all my {props.animal}s!
+
+ Here are all my {props.animal}
+ s!
+
{animals.map(animal => - {animal}
)}
)
@@ -270,6 +277,62 @@ const myNestedData = props => (
);
```
+### Debouncing Requests
+
+Some requests fire in response to a rapid succession of user events: things like autocomplete or resizing a window. For this reason, users sometimes need to wait until all the keystrokes are typed (until everything's _done_), before sending a request.
+
+Restful React exposes a `debounce` prop on `Get` that does exactly this.
+
+Here's an example:
+
+```jsx
+const SearchThis = props => (
+
+ {data => (
+
+
Here's all the things I search
+
{data.map(thing => - {thing}
)}
+
+ )}
+
+);
+```
+
+Debounce also accepts a number, which tells `Get` how long to wait until doing the request.
+
+```diff
+const SearchThis = props => (
+-
++
+ {data => (
+
+
Here's all the things I search
+
{data.map(thing => - {thing}
)}
+
+ )}
+
+);
+```
+
+It uses [lodash's debounce](https://lodash.com/docs/4.17.10#debounce) function under the hood, so you get all the benefits of it out of the box like so!
+
+```diff
+const SearchThis = props => (
+
+ {data => (
+
+
Here's all the things I search
+
{data.map(thing => - {thing}
)}
+
+ )}
+
+);
+```
+
### TypeScript Integration
One of the most poweful features of RESTful React, each component exported is strongly typed, empowering developers through self-documenting APIs. As for _returned_ data, simply tell your data prop _what_ you expect, and it'll be available to you throughout your usage of `children`.
diff --git a/src/Get.test.tsx b/src/Get.test.tsx
index 5a235fa4..eaa1532c 100644
--- a/src/Get.test.tsx
+++ b/src/Get.test.tsx
@@ -1,5 +1,6 @@
import "isomorphic-fetch";
import "jest-dom/extend-expect";
+import times from "lodash/times";
import nock from "nock";
import React from "react";
import { cleanup, render, wait } from "react-testing-library";
@@ -294,4 +295,66 @@ describe("Get", () => {
expect(children.mock.calls[3][0]).toEqual({ id: 2 });
});
});
+
+ describe("with debounce", () => {
+ it("should call the API only 1 time", async () => {
+ let apiCalls = 0;
+ nock("https://my-awesome-api.fake")
+ .filteringPath(/test=[^&]*/g, "test=XXX")
+ .get("/?test=XXX")
+ .reply(200, () => ++apiCalls)
+ .persist();
+
+ const children = jest.fn();
+ children.mockReturnValue();
+
+ const { rerender } = render(
+
+
+ {children}
+
+ ,
+ );
+
+ times(10, i =>
+ rerender(
+
+
+ {children}
+
+ ,
+ ),
+ );
+
+ await wait(() => expect(apiCalls).toEqual(1));
+ });
+
+ it("should call the API only 10 times without debounce", async () => {
+ let apiCalls = 0;
+ nock("https://my-awesome-api.fake")
+ .filteringPath(/test=[^&]*/g, "test=XXX")
+ .get("/?test=XXX")
+ .reply(200, () => ++apiCalls)
+ .persist();
+
+ const children = jest.fn();
+ children.mockReturnValue();
+
+ const { rerender } = render(
+
+ {children}
+ ,
+ );
+
+ times(10, i =>
+ rerender(
+
+ {children}
+ ,
+ ),
+ );
+
+ expect(apiCalls).toEqual(10);
+ });
+ });
});
diff --git a/src/Get.tsx b/src/Get.tsx
index e69a5657..6da6408c 100644
--- a/src/Get.tsx
+++ b/src/Get.tsx
@@ -1,3 +1,5 @@
+import { DebounceSettings } from "lodash";
+import debounce from "lodash/debounce";
import * as React from "react";
import RestfulReactProvider, { RestfulReactConsumer, RestfulReactProviderProps } from "./Context";
import { processResponse } from "./util/processResponse";
@@ -84,6 +86,17 @@ export interface GetProps {
*
*/
base?: string;
+ /**
+ * How long do we wait between subsequent requests?
+ * Uses [lodash's debounce](https://lodash.com/docs/4.17.10#debounce) under the hood.
+ */
+ debounce?:
+ | {
+ wait?: number;
+ options: DebounceSettings;
+ }
+ | boolean
+ | number;
}
/**
@@ -107,6 +120,18 @@ class ContextlessGet extends React.Component<
GetProps,
Readonly>
> {
+ constructor(props: GetProps) {
+ super(props);
+
+ if (typeof props.debounce === "object") {
+ this.fetch = debounce(this.fetch, props.debounce.wait, props.debounce.options);
+ } else if (typeof props.debounce === "number") {
+ this.fetch = debounce(this.fetch, props.debounce);
+ } else if (props.debounce) {
+ this.fetch = debounce(this.fetch);
+ }
+ }
+
public readonly state: Readonly> = {
data: null, // Means we don't _yet_ have data.
response: null,