use-async-effekt-hooks
Version:
React hooks for async effects and memoization with proper dependency tracking and linting support
600 lines (599 loc) • 37 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
import React, { Suspense, startTransition } from "react";
import { render, screen, waitFor, act as rtlAct } from "@testing-library/react";
import { act } from "./test-utils";
import { useAsyncMemoSuspense } from "../useAsyncMemoSuspense";
function TestComponent(_a) {
var factory = _a.factory, deps = _a.deps, scope = _a.scope, testId = _a.testId;
var value = useAsyncMemoSuspense(factory, deps, { scope: scope });
return (_jsxs("div", { "data-testid": testId || "test-result", children: ["Value: ", String(value)] }));
}
function UserProfile(_a) {
var _this = this;
var userId = _a.userId, _b = _a.includeDetails, includeDetails = _b === void 0 ? false : _b;
var user = useAsyncMemoSuspense(function () { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
// Simulate realistic API call
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 10); })];
case 1:
// Simulate realistic API call
_a.sent();
return [2 /*return*/, {
id: userId,
name: "User ".concat(userId),
email: "user".concat(userId, "@example.com"),
details: includeDetails ? "Details for ".concat(userId) : undefined,
}];
}
});
}); }, [userId, includeDetails], // Realistic dependency usage
{ scope: "user-profile-".concat(userId, "-").concat(includeDetails) } // Proper scoping
);
return (_jsxs("div", { "data-testid": "user-profile", children: [_jsx("h1", { children: user === null || user === void 0 ? void 0 : user.name }), _jsx("p", { children: user === null || user === void 0 ? void 0 : user.email }), (user === null || user === void 0 ? void 0 : user.details) && _jsx("p", { children: user.details })] }));
}
var ErrorBoundary = /** @class */ (function (_super) {
__extends(ErrorBoundary, _super);
function ErrorBoundary() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.state = { hasError: false, error: null };
return _this;
}
ErrorBoundary.getDerivedStateFromError = function (error) {
return { hasError: true, error: error };
};
ErrorBoundary.prototype.render = function () {
var _a;
if (this.state.hasError) {
return (_jsxs("div", { "data-testid": "error-boundary", children: ["Error: ", (_a = this.state.error) === null || _a === void 0 ? void 0 : _a.message] }));
}
return this.props.children;
};
return ErrorBoundary;
}(React.Component));
describe("useAsyncMemoSuspense - Core Functionality", function () {
var consoleErrorSpy;
beforeEach(function () {
consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(function () { });
jest.clearAllMocks();
jest.clearAllTimers();
jest.useFakeTimers();
});
afterEach(function () {
consoleErrorSpy.mockRestore();
jest.runOnlyPendingTimers();
jest.useRealTimers();
});
describe("Basic async operations", function () {
it("should suspend and then render resolved value", function () { return __awaiter(void 0, void 0, void 0, function () {
var factory;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory = jest
.fn()
.mockImplementation(function () {
return new Promise(function (resolve) {
return setTimeout(function () { return resolve("async-result"); }, 50);
});
});
render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestComponent, { factory: factory, deps: [], scope: "basic-async-test", testId: "async-result" }) }));
expect(screen.getByTestId("loading")).toBeInTheDocument();
expect(factory).toHaveBeenCalledTimes(1);
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.advanceTimersByTime(50);
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("async-result")).toHaveTextContent("Value: async-result");
})];
case 2:
_a.sent();
return [2 /*return*/];
}
});
}); });
it("should handle synchronous values without suspending", function () { return __awaiter(void 0, void 0, void 0, function () {
var factory;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory = jest.fn().mockReturnValue("sync-result");
render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestComponent, { factory: factory, deps: [], scope: "sync-test", testId: "sync-result" }) }));
// Wait a tick to ensure React has processed the synchronous result
return [4 /*yield*/, waitFor(function () {
expect(screen.queryByTestId("loading")).not.toBeInTheDocument();
expect(screen.getByTestId("sync-result")).toHaveTextContent("Value: sync-result");
})];
case 1:
// Wait a tick to ensure React has processed the synchronous result
_a.sent();
expect(factory).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
});
describe("Error handling", function () {
it("should throw async errors to error boundary", function () { return __awaiter(void 0, void 0, void 0, function () {
var error, factory;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
error = new Error("Async operation failed");
factory = jest.fn().mockRejectedValue(error);
render(_jsx(ErrorBoundary, { children: _jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestComponent, { factory: factory, deps: [], scope: "async-error-test" }) }) }));
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("error-boundary")).toHaveTextContent("Error: Async operation failed");
}, { timeout: 1000 })];
case 1:
_a.sent();
expect(factory).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
it("should throw sync errors to error boundary", function () {
var error = new Error("Sync operation failed");
var factory = jest.fn().mockImplementation(function () {
throw error;
});
render(_jsx(ErrorBoundary, { children: _jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestComponent, { factory: factory, deps: [], scope: "sync-error-test" }) }) }));
expect(screen.getByTestId("error-boundary")).toHaveTextContent("Error: Sync operation failed");
});
});
describe("Dependency management", function () {
it("should recompute when dependencies change", function () { return __awaiter(void 0, void 0, void 0, function () {
var rerender;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
rerender = render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(UserProfile, { userId: "1" }) })).rerender;
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByText("User 1")).toBeInTheDocument();
})];
case 1:
_a.sent();
rerender(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(UserProfile, { userId: "2" }) }));
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByText("User 2")).toBeInTheDocument();
})];
case 2:
_a.sent();
return [2 /*return*/];
}
});
}); });
it("should handle boolean dependency changes", function () { return __awaiter(void 0, void 0, void 0, function () {
var rerender;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
rerender = render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(UserProfile, { userId: "1", includeDetails: false }) })).rerender;
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByText("User 1")).toBeInTheDocument();
})];
case 1:
_a.sent();
expect(screen.queryByText("Details for 1")).not.toBeInTheDocument();
rerender(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(UserProfile, { userId: "1", includeDetails: true }) }));
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByText("Details for 1")).toBeInTheDocument();
})];
case 2:
_a.sent();
return [2 /*return*/];
}
});
}); });
});
describe("Scoping", function () {
it("should differentiate caches with different scopes", function () { return __awaiter(void 0, void 0, void 0, function () {
var factory1, factory2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory1 = jest.fn().mockResolvedValue("scoped-value-1");
factory2 = jest.fn().mockResolvedValue("scoped-value-2");
render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsxs("div", { children: [_jsx(TestComponent, { factory: factory1, deps: [], scope: "scope-1", testId: "result-1" }), _jsx(TestComponent, { factory: factory2, deps: [], scope: "scope-2", testId: "result-2" })] }) }));
return [4 /*yield*/, waitFor(function () {
expect(screen.getByTestId("result-1")).toHaveTextContent("Value: scoped-value-1");
expect(screen.getByTestId("result-2")).toHaveTextContent("Value: scoped-value-2");
})];
case 1:
_a.sent();
expect(factory1).toHaveBeenCalledTimes(1);
expect(factory2).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
});
describe("React 18 concurrent features", function () {
it("should work with startTransition", function () { return __awaiter(void 0, void 0, void 0, function () {
var resolver, factory, App, rerender;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory = jest
.fn()
.mockImplementationOnce(function () {
return new Promise(function (res) {
resolver = res;
});
})
.mockImplementationOnce(function () {
return new Promise(function (resolve) {
return setTimeout(function () { return resolve("transition-value-2"); }, 10);
});
});
App = function (_a) {
var dep = _a.dep;
var value = useAsyncMemoSuspense(factory, [dep], {
scope: "transition-test-".concat(dep),
});
return _jsxs("div", { "data-testid": "transition-result", children: ["Value: ", value] });
};
rerender = render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(App, { dep: 1 }) })).rerender;
expect(screen.getByTestId("loading")).toBeInTheDocument();
return [4 /*yield*/, rtlAct(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
resolver("transition-value-1");
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("transition-result")).toHaveTextContent("Value: transition-value-1");
})];
case 2:
_a.sent();
startTransition(function () {
rerender(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(App, { dep: 2 }) }));
});
// With startTransition, should show old value while loading
expect(screen.getByTestId("transition-result")).toHaveTextContent("Value: transition-value-1");
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.advanceTimersByTime(10);
return [2 /*return*/];
});
}); })];
case 3:
_a.sent();
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("transition-result")).toHaveTextContent("Value: transition-value-2");
})];
case 4:
_a.sent();
expect(factory).toHaveBeenCalledTimes(2);
return [2 /*return*/];
}
});
}); });
});
describe("Cache behavior and edge cases", function () {
it("should reuse cache when dependencies haven't changed", function () { return __awaiter(void 0, void 0, void 0, function () {
var factory, TestCacheComponent, rerender;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory = jest.fn().mockResolvedValue("cached-result");
TestCacheComponent = function (_a) {
var rerender = _a.rerender;
var value = useAsyncMemoSuspense(factory, ["static-dep"], {
scope: "cache-test",
});
return (_jsxs("div", { "data-testid": "cache-result", children: ["Value: ", value, " - Render: ", rerender ? "second" : "first"] }));
};
rerender = render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestCacheComponent, { rerender: false }) })).rerender;
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("cache-result")).toHaveTextContent("Value: cached-result - Render: first");
})];
case 1:
_a.sent();
expect(factory).toHaveBeenCalledTimes(1);
// Rerender with same dependencies - should use cache
rerender(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestCacheComponent, { rerender: true }) }));
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("cache-result")).toHaveTextContent("Value: cached-result - Render: second");
})];
case 2:
_a.sent();
// Factory should still only be called once due to caching
expect(factory).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
it("should handle multiple components with same cache key", function () { return __awaiter(void 0, void 0, void 0, function () {
var factory, ComponentA, ComponentB;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory = jest.fn().mockResolvedValue("shared-result");
ComponentA = function () {
var value = useAsyncMemoSuspense(factory, ["shared"], {
scope: "shared-scope",
});
return _jsxs("div", { "data-testid": "component-a", children: ["A: ", value] });
};
ComponentB = function () {
var value = useAsyncMemoSuspense(factory, ["shared"], {
scope: "shared-scope",
});
return _jsxs("div", { "data-testid": "component-b", children: ["B: ", value] });
};
render(_jsxs(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: [_jsx(ComponentA, {}), _jsx(ComponentB, {})] }));
return [4 /*yield*/, waitFor(function () {
expect(screen.getByTestId("component-a")).toHaveTextContent("A: shared-result");
expect(screen.getByTestId("component-b")).toHaveTextContent("B: shared-result");
})];
case 1:
_a.sent();
// Factory should only be called once since both components share the same cache
expect(factory).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
it("should handle complex dependency types", function () { return __awaiter(void 0, void 0, void 0, function () {
var complexObj, factory, TestComplexDeps, rerender;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
complexObj = { nested: { value: 42 }, array: [1, 2, 3] };
factory = jest.fn().mockResolvedValue("complex-deps-result");
TestComplexDeps = function (_a) {
var obj = _a.obj, num = _a.num, str = _a.str, bool = _a.bool, nullVal = _a.nullVal, undefinedVal = _a.undefinedVal;
var value = useAsyncMemoSuspense(factory, [obj, num, str, bool, nullVal, undefinedVal], { scope: "complex-deps" });
return _jsxs("div", { "data-testid": "complex-result", children: ["Value: ", value] });
};
rerender = render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestComplexDeps, { obj: complexObj, num: 42, str: "test", bool: true, nullVal: null, undefinedVal: undefined }) })).rerender;
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("complex-result")).toHaveTextContent("Value: complex-deps-result");
})];
case 1:
_a.sent();
expect(factory).toHaveBeenCalledTimes(1);
// Rerender with same complex dependencies - should use cache
rerender(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestComplexDeps, { obj: complexObj, num: 42, str: "test", bool: true, nullVal: null, undefinedVal: undefined }) }));
// Should still use cache
expect(factory).toHaveBeenCalledTimes(1);
// Change one dependency - should invalidate cache
rerender(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestComplexDeps, { obj: complexObj, num: 43, str: "test", bool: true, nullVal: null, undefinedVal: undefined }) }));
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("complex-result")).toHaveTextContent("Value: complex-deps-result");
})];
case 2:
_a.sent();
expect(factory).toHaveBeenCalledTimes(2);
return [2 /*return*/];
}
});
}); });
it("should handle empty dependencies array", function () { return __awaiter(void 0, void 0, void 0, function () {
var factory, TestEmptyDeps;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory = jest.fn().mockResolvedValue("empty-deps-result");
TestEmptyDeps = function () {
var value = useAsyncMemoSuspense(factory, [], {
scope: "empty-deps-test",
});
return _jsxs("div", { "data-testid": "empty-deps-result", children: ["Value: ", value] });
};
render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestEmptyDeps, {}) }));
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("empty-deps-result")).toHaveTextContent("Value: empty-deps-result");
})];
case 1:
_a.sent();
expect(factory).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
it("should handle no dependencies parameter (default empty array)", function () { return __awaiter(void 0, void 0, void 0, function () {
var factory, TestNoDeps;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory = jest.fn().mockResolvedValue("no-deps-result");
TestNoDeps = function () {
// Test calling without deps parameter
var value = useAsyncMemoSuspense(factory, undefined, {
scope: "no-deps-test",
});
return _jsxs("div", { "data-testid": "no-deps-result", children: ["Value: ", value] });
};
render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestNoDeps, {}) }));
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("no-deps-result")).toHaveTextContent("Value: no-deps-result");
})];
case 1:
_a.sent();
expect(factory).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
it("should handle zero and negative zero in dependencies", function () { return __awaiter(void 0, void 0, void 0, function () {
var factory, TestZero, rerender;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory = jest.fn().mockResolvedValue("zero-result");
TestZero = function (_a) {
var zeroValue = _a.zeroValue;
var value = useAsyncMemoSuspense(factory, [zeroValue], {
scope: "zero-test",
});
return _jsxs("div", { "data-testid": "zero-result", children: ["Value: ", value] });
};
rerender = render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestZero, { zeroValue: 0 }) })).rerender;
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("zero-result")).toHaveTextContent("Value: zero-result");
})];
case 1:
_a.sent();
expect(factory).toHaveBeenCalledTimes(1);
// Rerender with -0 - should be treated as different due to Object.is(0, -0) being false
rerender(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestZero, { zeroValue: -0 }) }));
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("zero-result")).toHaveTextContent("Value: zero-result");
})];
case 2:
_a.sent();
// Should be called twice since Object.is(0, -0) is false
expect(factory).toHaveBeenCalledTimes(2);
return [2 /*return*/];
}
});
}); });
it("should handle functions with identical string representations but different behavior", function () { return __awaiter(void 0, void 0, void 0, function () {
var factory1, factory2, TestIdenticalFunctions, rerender;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory1 = jest.fn().mockResolvedValue("factory1-result");
factory2 = jest.fn().mockResolvedValue("factory2-result");
TestIdenticalFunctions = function (_a) {
var useFirst = _a.useFirst;
var factory = useFirst ? factory1 : factory2;
var value = useAsyncMemoSuspense(factory, [useFirst]);
return _jsxs("div", { "data-testid": "identical-result", children: ["Value: ", value] });
};
rerender = render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestIdenticalFunctions, { useFirst: true }) })).rerender;
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("identical-result")).toHaveTextContent("Value: factory1-result");
})];
case 1:
_a.sent();
expect(factory1).toHaveBeenCalledTimes(1);
rerender(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestIdenticalFunctions, { useFirst: false }) }));
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("identical-result")).toHaveTextContent("Value: factory2-result");
})];
case 2:
_a.sent();
expect(factory2).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
});
// Note: Error recovery is already tested in the main error handling tests
describe("Performance and memory considerations", function () {
it("should handle rapid dependency changes efficiently", function () { return __awaiter(void 0, void 0, void 0, function () {
var factory, TestRapidChanges, rerender, _loop_1, i;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
factory = jest
.fn()
.mockImplementation(function (dep) { return Promise.resolve("rapid-".concat(dep)); });
TestRapidChanges = function (_a) {
var dep = _a.dep;
var value = useAsyncMemoSuspense(function () { return factory(dep); }, [dep], {
scope: "rapid-test",
});
return _jsxs("div", { "data-testid": "rapid-result", children: ["Value: ", value] });
};
rerender = render(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestRapidChanges, { dep: 1 }) })).rerender;
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("rapid-result")).toHaveTextContent("Value: rapid-1");
})];
case 1:
_a.sent();
_loop_1 = function (i) {
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
rerender(_jsx(Suspense, { fallback: _jsx("div", { "data-testid": "loading", children: "Loading..." }), children: _jsx(TestRapidChanges, { dep: i }) }));
return [4 /*yield*/, waitFor(function () {
return expect(screen.getByTestId("rapid-result")).toHaveTextContent("Value: rapid-".concat(i));
})];
case 1:
_b.sent();
return [2 /*return*/];
}
});
};
i = 2;
_a.label = 2;
case 2:
if (!(i <= 5)) return [3 /*break*/, 5];
return [5 /*yield**/, _loop_1(i)];
case 3:
_a.sent();
_a.label = 4;
case 4:
i++;
return [3 /*break*/, 2];
case 5:
expect(factory).toHaveBeenCalledTimes(5);
return [2 /*return*/];
}
});
}); });
});
});