Skip to content
This repository has been archived by the owner on Nov 11, 2023. It is now read-only.

Refetch when resolve prop changes #70

Merged
merged 3 commits into from
Oct 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 127 additions & 2 deletions src/Get.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ describe("Get", () => {

describe("with wait", () => {
it("should render nothing if until we have data", async () => {
nock("https://my-awesome-api.fake")
.get("/")
.delay(1000)
.reply(200, { hello: "world" });

const children = jest.fn();
children.mockReturnValue(<div />);

Expand Down Expand Up @@ -521,15 +526,25 @@ describe("Get", () => {
const children = jest.fn();
children.mockReturnValue(<div />);

const resolve = a => a;

/**
* A new instance of RestfulProvider is created on every rerender.
* This will create a new resolve function every time forcing Get to
* refetch.
* In a real app, only Get would be rerendered so resolve would be the
* same on every new render. To mimic that behavior, resolve is created
* ahead so Get will get the same instance on every rerender.
*/
const { rerender } = render(
<RestfulProvider base="https://my-awesome-api.fake">
<RestfulProvider base="https://my-awesome-api.fake" resolve={resolve}>
<Get path="?test=1">{children}</Get>
</RestfulProvider>,
);

times(10, i =>
rerender(
<RestfulProvider base="https://my-awesome-api.fake">
<RestfulProvider base="https://my-awesome-api.fake" resolve={resolve}>
<Get path={`?test=${i + 1}`}>{children}</Get>
</RestfulProvider>,
),
Expand All @@ -538,4 +553,114 @@ describe("Get", () => {
expect(apiCalls).toEqual(10);
});
});
describe("refetch after update", () => {
it("should not refetch when base, path or resolve don't change", () => {
let apiCalls = 0;
nock("https://my-awesome-api.fake")
.get("/")
.reply(200, () => ++apiCalls)
.persist();

const children = jest.fn();
children.mockReturnValue(<div />);

const resolve = a => a;
const { rerender } = render(
<RestfulProvider base="https://my-awesome-api.fake" resolve={resolve}>
<Get path="">{children}</Get>
</RestfulProvider>,
);

rerender(
<RestfulProvider base="https://my-awesome-api.fake" resolve={resolve}>
<Get path="">{children}</Get>
</RestfulProvider>,
);

expect(apiCalls).toEqual(1);
});
it("should refetch when base changes", () => {
let apiCalls = 0;
nock("https://my-awesome-api.fake")
.get("/")
.reply(200, () => ++apiCalls);

const children = jest.fn();
children.mockReturnValue(<div />);

const resolve = a => a;
const { rerender } = render(
<RestfulProvider base="https://my-awesome-api.fake" resolve={resolve}>
<Get path="">{children}</Get>
</RestfulProvider>,
);

nock("https://my-new-api.fake")
.get("/")
.reply(200, () => ++apiCalls);
rerender(
<RestfulProvider base="https://my-awesome-api.fake" resolve={resolve}>
<Get base="https://my-new-api.fake" path="">
{children}
</Get>
</RestfulProvider>,
);

expect(apiCalls).toEqual(2);
});
it("should refetch when path changes", () => {
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(<div />);

const resolve = a => a;
const { rerender } = render(
<RestfulProvider base="https://my-awesome-api.fake" resolve={resolve}>
<Get path="/?test=0">{children}</Get>
</RestfulProvider>,
);

rerender(
<RestfulProvider base="https://my-awesome-api.fake" resolve={resolve}>
<Get path="/?test=1">{children}</Get>
</RestfulProvider>,
);

expect(apiCalls).toEqual(2);
});
it("should refetch when resolve changes", () => {
let apiCalls = 0;
nock("https://my-awesome-api.fake")
.get("/")
.reply(200, () => ++apiCalls)
.persist();

const children = jest.fn();
children.mockReturnValue(<div />);

const providerResolve = a => a;
const { rerender } = render(
<RestfulProvider base="https://my-awesome-api.fake" resolve={providerResolve}>
<Get path="">{children}</Get>
</RestfulProvider>,
);

const newResolve = a => a;
rerender(
<RestfulProvider base="https://my-awesome-api.fake" resolve={providerResolve}>
<Get path="" resolve={newResolve}>
{children}
</Get>
</RestfulProvider>,
);

expect(apiCalls).toEqual(2);
});
});
});
5 changes: 2 additions & 3 deletions src/Get.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,8 @@ class ContextlessGet<TData, TError> extends React.Component<
}

public componentDidUpdate(prevProps: GetProps<TData, TError>) {
// If the path or base prop changes, refetch!
const { path, base } = this.props;
if (prevProps.path !== path || prevProps.base !== base) {
const { base, path, resolve } = prevProps;
if (base !== this.props.base || path !== this.props.path || resolve !== this.props.resolve) {
if (!this.props.lazy) {
this.fetch();
}
Expand Down
31 changes: 31 additions & 0 deletions src/Poll.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,37 @@ describe("Poll", () => {
await wait(() => expect(children.mock.calls.length).toBe(3));
expect(children.mock.calls[2][0]).toEqual({ data: "hello you" });
});

it("should update data when resolver changes", async () => {
nock("https://my-awesome-api.fake")
.get("/")
.reply(200, { hello: "world" });

const children = jest.fn();
children.mockReturnValue(<div />);

const resolve = data => ({ ...data, too: "bar" });
const newResolve = data => ({ ...data, foo: "bar" });

const { rerender } = render(
<RestfulProvider base="https://my-awesome-api.fake">
<Poll path="" resolve={resolve}>
{children}
</Poll>
</RestfulProvider>,
);

rerender(
<RestfulProvider base="https://my-awesome-api.fake">
<Poll path="" resolve={newResolve}>
{children}
</Poll>
</RestfulProvider>,
);

await wait(() => expect(children.mock.calls.length).toBe(3));
expect(children.mock.calls[2][0]).toEqual({ hello: "world", foo: "bar" });
});
});

describe("with lazy", () => {
Expand Down
19 changes: 13 additions & 6 deletions src/Poll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ export interface PollState<TData, TError> {
* What data are we holding in here?
*/
data: GetState<TData, TError>["data"];
/**
* What data did we had before?
*/
previousData: GetState<TData, TError>["data"];
/**
* Are we loading?
*/
Expand All @@ -151,6 +155,7 @@ class ContextlessPoll<TData, TError> extends React.Component<
> {
public readonly state: Readonly<PollState<TData, TError>> = {
data: null,
previousData: null,
loading: !this.props.lazy,
lastResponse: null,
polling: !this.props.lazy,
Expand Down Expand Up @@ -205,7 +210,7 @@ class ContextlessPoll<TData, TError> extends React.Component<
}

// If we should keep going,
const { base, path, resolve, interval, wait } = this.props;
const { base, path, interval, wait } = this.props;
const { lastPollIndex } = this.state;
const requestOptions = this.getRequestOptions();

Expand Down Expand Up @@ -240,7 +245,8 @@ class ContextlessPoll<TData, TError> extends React.Component<
this.setState(prevState => ({
loading: false,
lastResponse: response,
data: resolve ? resolve(data, prevState.data) : data,
previousData: prevState.data,
data,
error: null,
lastPollIndex: response.headers.get("x-polling-index") || undefined,
}));
Expand Down Expand Up @@ -290,8 +296,8 @@ class ContextlessPoll<TData, TError> extends React.Component<
}

public render() {
const { lastResponse: response, data, polling, loading, error, finished } = this.state;
const { children, base, path } = this.props;
const { lastResponse: response, previousData, data, polling, loading, error, finished } = this.state;
const { children, base, path, resolve } = this.props;

const meta: Meta = {
response,
Expand All @@ -309,8 +315,9 @@ class ContextlessPoll<TData, TError> extends React.Component<
stop: this.stop,
start: this.start,
};

return children(data, states, actions, meta);
// data is parsed only when poll has already resolved so response is defined
const resolvedData = response && resolve ? resolve(data, previousData) : data;
return children(resolvedData, states, actions, meta);
}
}

Expand Down