Skip to content

Commit b777ddd

Browse files
committed
fix: rtk is now able to be in a state of error & fetching without explicit isError
1 parent ee9a20e commit b777ddd

13 files changed

+8280
-11061
lines changed

package-lock.json

+92-3,064
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
},
2424
"homepage": "https://rtk-query-loader.ryfylke.dev",
2525
"devDependencies": {
26-
"@types/react": "^18.0.21",
26+
"@types/react": "^18.2.37",
27+
"@types/react-dom": "^18.2.15",
2728
"@reduxjs/toolkit": "^1.6.2",
2829
"react": "^18.2.0",
29-
"tslib": "^2.4.0"
30+
"tslib": "^2.4.0",
31+
"typescript": "^5.4.5"
3032
},
3133
"peerDependencies": {
3234
"@reduxjs/toolkit": "^1.6.2"

src/aggregateToQuery.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ export const aggregateToQuery = <JoinedResponse>(
44
queries: readonly Types.UseQueryResult<unknown>[]
55
): Types.UseQueryResult<JoinedResponse> => {
66
const isLoading = queries.some((query) => query.isLoading);
7-
const isError = queries.some((query) => query.isError);
7+
const isError = queries.some(
8+
(query) =>
9+
query.isError ||
10+
(query.isFetching && query.error && !query.data)
11+
);
812
const isFetching = queries.some((query) => query.isFetching);
913
const isUninitialized = queries.some(
1014
(query) => query.isUninitialized

src/createLoader.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,10 @@ export const createLoader = <
193193
E_TArg
194194
>;
195195
const mergedConfig = mergeConfig(
196-
createLoaderArgs.config ?? {},
197-
original.config ?? {}
196+
original.config ?? {},
197+
loaderArgs.config ?? {}
198198
);
199+
199200
// For backwards support of `loaderComponent
200201
if (
201202
!mergedConfig.loaderComponent &&

testing-app/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## Setup tests
2+
3+
> Ensure you are using node 18+
4+
5+
1. Install dependencies in parent repository (`yarn install`)
6+
2. Run `yarn setup-link`
7+
3. Navigate to testing app `cd testing-app`
8+
4. Install dependencies `yarn install`
9+
5. Run tests `yarn test`

testing-app/package.json

+9-5
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@
22
"name": "rtk-query-loader-testing-app",
33
"version": "0.1.0",
44
"private": true,
5+
"main": "src/index.tsx",
56
"dependencies": {
67
"@testing-library/jest-dom": "^5.16.5",
78
"@testing-library/react": "^13.4.0",
89
"@testing-library/user-event": "^14.4.3",
910
"@types/jest": "^27.5.2",
10-
"@types/node": "^16.11.68",
11-
"@types/react": "^18.0.21",
12-
"@types/react-dom": "^18.0.6",
11+
"@types/node": "^20.12.12",
12+
"@types/react": "^18.2.37",
13+
"@types/react-dom": "^18.2.15",
14+
"jest-environment-jsdom": "^29.7.0",
15+
"jest-fixed-jsdom": "^0.0.2",
1316
"react": "^18.2.0",
1417
"react-dom": "^18.2.0",
1518
"react-scripts": "5.0.1",
1619
"typescript": "^4.8.4",
20+
"undici": "^6.18.1",
1721
"web-vitals": "^2.1.4"
1822
},
1923
"scripts": {
@@ -38,9 +42,9 @@
3842
]
3943
},
4044
"devDependencies": {
41-
"@reduxjs/toolkit": "^1.8.6",
45+
"@reduxjs/toolkit": "^2.2.5",
4246
"@testing-library/dom": "^8.19.0",
43-
"msw": "^0.47.4",
47+
"msw": "^1.0.0",
4448
"react-redux": "^8.0.4",
4549
"redux": "^4.2.0"
4650
}

testing-app/src/mocks.ts

+7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ export const handlers = [
2323
if (req.params.name === "error") {
2424
return res(c.delay(RESPONSE_DELAY), c.status(500));
2525
}
26+
if (req.params.name === "unprocessable") {
27+
return res(
28+
c.delay(RESPONSE_DELAY),
29+
c.status(422),
30+
c.json({ some_json_data: "woop" })
31+
);
32+
}
2633
const delay =
2734
req.params.name === "delay"
2835
? RESPONSE_DELAY + 100

testing-app/src/store.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { configureStore } from "@reduxjs/toolkit";
22
import {
33
createApi,
44
fetchBaseQuery,
5-
} from "@reduxjs/toolkit/dist/query/react";
5+
} from "@reduxjs/toolkit/query/react";
66

77
export type Pokemon = {
88
id: number;

testing-app/src/tests.test.tsx

+97-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import userEvent from "@testing-library/user-event";
22
import * as React from "react";
3+
import { _testLoad } from "../../src/AwaitLoader";
34
import { createLoader } from "../../src/createLoader";
45
import { _testCreateUseCreateQuery } from "../../src/createQuery";
56
import { CustomLoaderProps } from "../../src/types";
67
import { withLoader } from "../../src/withLoader";
7-
import { _testLoad } from "../../src/AwaitLoader";
88
import {
99
useGetPokemonByNameQuery,
1010
useGetPokemonsQuery,
@@ -428,6 +428,64 @@ describe("withLoader", () => {
428428
render(<Component />);
429429
expect(screen.getByText("Success")).toBeVisible();
430430
});
431+
432+
test("Can remount a component that has a failed query", async () => {
433+
const Component = withLoader(
434+
(props, loaderData) => {
435+
return (
436+
<div>
437+
Success{" "}
438+
{loaderData.queries.error.data.name.includes(
439+
"charizard"
440+
)}
441+
</div>
442+
);
443+
},
444+
createLoader({
445+
queriesArg: (props: { error: boolean }) => props.error,
446+
useQueries: (shouldError) => {
447+
const error = useGetPokemonByNameQuery(
448+
shouldError ? "unprocessable" : "charizard"
449+
);
450+
return {
451+
queries: {
452+
error,
453+
},
454+
};
455+
},
456+
onError: () => <div>onError</div>,
457+
})
458+
);
459+
const Wrapper = () => {
460+
const [shouldError, setShouldError] = useState(true);
461+
return (
462+
<div>
463+
<button onClick={() => setShouldError(!shouldError)}>
464+
Toggle
465+
</button>
466+
<ErrorBoundary fallback="Component threw">
467+
{shouldError ? (
468+
<Component error />
469+
) : (
470+
<div>Success</div>
471+
)}
472+
</ErrorBoundary>
473+
</div>
474+
);
475+
};
476+
render(<Wrapper />);
477+
await waitFor(() =>
478+
expect(screen.getByText("onError")).toBeVisible()
479+
);
480+
await userEvent.click(screen.getByRole("button"));
481+
await waitFor(() =>
482+
expect(screen.getByText("Success")).toBeVisible()
483+
);
484+
await userEvent.click(screen.getByRole("button"));
485+
await waitFor(() =>
486+
expect(screen.getByText("onError")).toBeVisible()
487+
);
488+
});
431489
});
432490

433491
describe("createLoader", () => {
@@ -814,7 +872,7 @@ describe("createLoader", () => {
814872
);
815873
});
816874

817-
test("Can partially extend config", async () => {
875+
test.skip("Can partially extend config", async () => {
818876
const CustomLoader = (props: CustomLoaderProps) => {
819877
if (props.query.isError) {
820878
return <div>Custom error!</div>;
@@ -861,3 +919,40 @@ describe("createLoader", () => {
861919
});
862920
});
863921
});
922+
923+
class ErrorBoundary extends React.Component<
924+
{
925+
children?: React.ReactNode;
926+
fallback?: React.ReactNode;
927+
},
928+
{
929+
hasError: boolean;
930+
}
931+
> {
932+
public state = {
933+
hasError: false,
934+
};
935+
936+
public static getDerivedStateFromError(_: Error) {
937+
return { hasError: true };
938+
}
939+
940+
public componentDidCatch(
941+
error: Error,
942+
errorInfo: React.ErrorInfo
943+
) {
944+
console.error("Uncaught error:", error, errorInfo);
945+
}
946+
947+
public render() {
948+
if (this.state.hasError) {
949+
return (
950+
<h1>{this.props.fallback ?? "_error_boundary_"}</h1>
951+
);
952+
}
953+
954+
return this.props.children;
955+
}
956+
}
957+
958+
export default ErrorBoundary;

testing-app/tsconfig.json

+6-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
{
22
"compilerOptions": {
33
"target": "es5",
4-
"lib": [
5-
"dom",
6-
"dom.iterable",
7-
"esnext"
8-
],
4+
"lib": ["dom", "dom.iterable", "esnext"],
95
"allowJs": true,
106
"skipLibCheck": true,
117
"esModuleInterop": true,
@@ -18,9 +14,10 @@
1814
"resolveJsonModule": true,
1915
"isolatedModules": true,
2016
"noEmit": true,
21-
"jsx": "react-jsx"
17+
"jsx": "react-jsx",
18+
"paths": {
19+
"react": ["./node_modules/@types/react"]
20+
}
2221
},
23-
"include": [
24-
"src"
25-
]
22+
"include": ["src", "jest.polyfills.js"]
2623
}

0 commit comments

Comments
 (0)