use-async-effekt-hooks
Version:
React hooks for async effects and memoization with proper dependency tracking and linting support
704 lines (703 loc) • 36.4 kB
JavaScript
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 { renderHook, act } from "./test-utils";
import { useAsyncEffekt } from "../useAsyncEffekt";
// Helper to create a delay
var delay = function (ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms); }); };
describe("useAsyncEffekt", function () {
beforeEach(function () {
jest.clearAllMocks();
jest.clearAllTimers();
jest.useFakeTimers();
});
afterEach(function () {
jest.runOnlyPendingTimers();
jest.useRealTimers();
});
it("should execute async effect on mount", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockEffect;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect = jest.fn().mockResolvedValue(undefined);
renderHook(function () { return useAsyncEffekt(mockEffect, []); });
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
expect(mockEffect).toHaveBeenCalledWith({
isMounted: expect.any(Function),
waitForPrevious: expect.any(Function),
});
return [2 /*return*/];
}
});
}); });
it("should provide isMounted function that returns true when mounted", function () { return __awaiter(void 0, void 0, void 0, function () {
var isMountedFn, mockEffect;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect = jest.fn().mockImplementation(function (_a) {
var isMounted = _a.isMounted;
isMountedFn = isMounted;
return Promise.resolve();
});
renderHook(function () { return useAsyncEffekt(mockEffect, []); });
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(isMountedFn).toBeDefined();
expect(isMountedFn()).toBe(true);
return [2 /*return*/];
}
});
}); });
it("should provide isMounted function that returns false when unmounted", function () { return __awaiter(void 0, void 0, void 0, function () {
var isMountedFn, mockEffect, unmount;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect = jest.fn().mockImplementation(function (_a) {
var isMounted = _a.isMounted;
isMountedFn = isMounted;
return Promise.resolve();
});
unmount = renderHook(function () { return useAsyncEffekt(mockEffect, []); }).unmount;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
unmount();
expect(isMountedFn).toBeDefined();
expect(isMountedFn()).toBe(false);
return [2 /*return*/];
}
});
}); });
it("should re-run effect when dependencies change", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockEffect, dep, rerender;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect = jest.fn().mockResolvedValue(undefined);
dep = 1;
rerender = renderHook(function () { return useAsyncEffekt(mockEffect, [dep]); }).rerender;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
dep = 2;
rerender();
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(2);
return [2 /*return*/];
}
});
}); });
it("should not re-run effect when dependencies stay the same", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockEffect, dep, rerender;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect = jest.fn().mockResolvedValue(undefined);
dep = 1;
rerender = renderHook(function () { return useAsyncEffekt(mockEffect, [dep]); }).rerender;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
rerender();
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
it("should handle synchronous cleanup function", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockCleanup, mockEffect, unmount;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockCleanup = jest.fn();
mockEffect = jest.fn().mockResolvedValue(mockCleanup);
unmount = renderHook(function () { return useAsyncEffekt(mockEffect, []); }).unmount;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
expect(mockCleanup).not.toHaveBeenCalled();
unmount();
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
expect(mockCleanup).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
it("should handle asynchronous cleanup function", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockCleanup, mockEffect, unmount;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockCleanup = jest.fn().mockResolvedValue(undefined);
mockEffect = jest.fn().mockResolvedValue(mockCleanup);
unmount = renderHook(function () { return useAsyncEffekt(mockEffect, []); }).unmount;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
expect(mockCleanup).not.toHaveBeenCalled();
unmount();
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
expect(mockCleanup).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
it("should wait for previous effect when waitForPrevious is called", function () { return __awaiter(void 0, void 0, void 0, function () {
var resolveFirstEffect, fistEffectFinishedAndCleaned, firstEffect, secondEffect, dep, rerender;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
fistEffectFinishedAndCleaned = false;
firstEffect = jest.fn().mockImplementation(function () {
return new Promise(function (resolve) {
resolveFirstEffect = resolve;
});
});
secondEffect = jest
.fn()
.mockImplementation(function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
var waitForPrevious = _b.waitForPrevious;
return __generator(this, function (_c) {
switch (_c.label) {
case 0: return [4 /*yield*/, waitForPrevious()];
case 1:
_c.sent();
fistEffectFinishedAndCleaned = true;
return [2 /*return*/];
}
});
}); });
dep = 1;
rerender = renderHook(function () {
return useAsyncEffekt(dep === 1 ? firstEffect : secondEffect, [dep]);
}).rerender;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(firstEffect).toHaveBeenCalledTimes(1);
expect(fistEffectFinishedAndCleaned).toBe(false);
// Change dependency to trigger second effect
dep = 2;
rerender();
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
// Second effect should be waiting
expect(secondEffect).toHaveBeenCalledTimes(1);
expect(fistEffectFinishedAndCleaned).toBe(false);
// Resolve first effect
resolveFirstEffect();
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 3:
_a.sent();
expect(fistEffectFinishedAndCleaned).toBe(true);
return [2 /*return*/];
}
});
}); });
it("should handle errors in async effects gracefully", function () { return __awaiter(void 0, void 0, void 0, function () {
var consoleErrorSpy, error, mockEffect;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
error = new Error("Test error");
mockEffect = jest.fn().mockRejectedValue(error);
renderHook(function () { return useAsyncEffekt(mockEffect, []); });
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
expect(consoleErrorSpy).toHaveBeenCalledWith("useAsyncEffekt error:", error);
consoleErrorSpy.mockRestore();
return [2 /*return*/];
}
});
}); });
it("should handle errors in cleanup functions gracefully", function () { return __awaiter(void 0, void 0, void 0, function () {
var consoleErrorSpy, error, mockCleanup, mockEffect, unmount;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
error = new Error("Cleanup error");
mockCleanup = jest.fn().mockRejectedValue(error);
mockEffect = jest.fn().mockResolvedValue(mockCleanup);
unmount = renderHook(function () { return useAsyncEffekt(mockEffect, []); }).unmount;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
unmount();
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
expect(consoleErrorSpy).toHaveBeenCalledWith("useAsyncEffekt cleanup error:", error);
consoleErrorSpy.mockRestore();
return [2 /*return*/];
}
});
}); });
it("should cancel effect if component unmounts before effect completes", function () { return __awaiter(void 0, void 0, void 0, function () {
var effectCompleted, mockEffect, unmount;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
effectCompleted = false;
mockEffect = jest.fn().mockImplementation(function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
var isMounted = _b.isMounted;
return __generator(this, function (_c) {
switch (_c.label) {
case 0: return [4 /*yield*/, delay(100)];
case 1:
_c.sent();
if (isMounted()) {
effectCompleted = true;
}
return [2 /*return*/];
}
});
}); });
unmount = renderHook(function () { return useAsyncEffekt(mockEffect, []); }).unmount;
// Start the effect
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:
// Start the effect
_a.sent();
// Unmount before effect completes
unmount();
// Complete the timer
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.advanceTimersByTime(100);
return [2 /*return*/];
});
}); })];
case 2:
// Complete the timer
_a.sent();
expect(effectCompleted).toBe(false);
return [2 /*return*/];
}
});
}); });
it("should work without dependencies array", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockEffect;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect = jest.fn().mockResolvedValue(undefined);
renderHook(function () { return useAsyncEffekt(mockEffect); });
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
it("should handle rapid dependency changes correctly", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockEffect, dep, rerender, i;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect = jest.fn().mockResolvedValue(undefined);
dep = 1;
rerender = renderHook(function () { return useAsyncEffekt(mockEffect, [dep]); }).rerender;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
// Rapidly change dependencies
for (i = 2; i <= 5; i++) {
dep = i;
rerender();
}
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
// Should have been called for each dependency change
expect(mockEffect).toHaveBeenCalledTimes(5);
return [2 /*return*/];
}
});
}); });
it("should handle effect that throws synchronously", function () { return __awaiter(void 0, void 0, void 0, function () {
var consoleErrorSpy, syncError, mockEffect;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
consoleErrorSpy = jest
.spyOn(console, "error")
.mockImplementation(function () { });
syncError = new Error("Synchronous error");
mockEffect = jest.fn().mockImplementation(function () {
throw syncError;
});
renderHook(function () { return useAsyncEffekt(mockEffect, []); });
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
expect(consoleErrorSpy).toHaveBeenCalledWith("useAsyncEffekt error:", syncError);
consoleErrorSpy.mockRestore();
return [2 /*return*/];
}
});
}); });
it("should handle cleanup function that throws", function () { return __awaiter(void 0, void 0, void 0, function () {
var consoleErrorSpy, cleanupError, mockCleanup, mockEffect, unmount;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
consoleErrorSpy = jest
.spyOn(console, "error")
.mockImplementation(function () { });
cleanupError = new Error("Cleanup error");
mockCleanup = jest.fn().mockImplementation(function () {
throw cleanupError;
});
mockEffect = jest.fn().mockResolvedValue(mockCleanup);
unmount = renderHook(function () { return useAsyncEffekt(mockEffect, []); }).unmount;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
unmount();
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
expect(mockCleanup).toHaveBeenCalledTimes(1);
expect(consoleErrorSpy).toHaveBeenCalledWith("useAsyncEffekt cleanup error:", cleanupError);
consoleErrorSpy.mockRestore();
return [2 /*return*/];
}
});
}); });
it("should handle async cleanup function that rejects", function () { return __awaiter(void 0, void 0, void 0, function () {
var consoleErrorSpy, cleanupError, mockCleanup, mockEffect, unmount;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
consoleErrorSpy = jest
.spyOn(console, "error")
.mockImplementation(function () { });
cleanupError = new Error("Async cleanup error");
mockCleanup = jest.fn().mockRejectedValue(cleanupError);
mockEffect = jest.fn().mockResolvedValue(mockCleanup);
unmount = renderHook(function () { return useAsyncEffekt(mockEffect, []); }).unmount;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
unmount();
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
expect(mockCleanup).toHaveBeenCalledTimes(1);
expect(consoleErrorSpy).toHaveBeenCalledWith("useAsyncEffekt cleanup error:", cleanupError);
consoleErrorSpy.mockRestore();
return [2 /*return*/];
}
});
}); });
it("should handle multiple rapid dependency changes", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockEffect, dep, rerender, i;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect = jest.fn().mockResolvedValue(undefined);
dep = 1;
rerender = renderHook(function () { return useAsyncEffekt(mockEffect, [dep]); }).rerender;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
// Rapid changes
for (i = 2; i <= 5; i++) {
dep = i;
rerender();
}
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
// Should handle all changes properly
expect(mockEffect).toHaveBeenCalledTimes(5);
return [2 /*return*/];
}
});
}); });
it("should handle effect that returns null", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockEffect, unmount;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect = jest.fn().mockResolvedValue(null);
unmount = renderHook(function () { return useAsyncEffekt(mockEffect, []); }).unmount;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
// Should not throw when unmounting
expect(function () { return unmount(); }).not.toThrow();
return [2 /*return*/];
}
});
}); });
it("should handle undefined dependencies", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockEffect;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect = jest.fn().mockResolvedValue(undefined);
renderHook(function () { return useAsyncEffekt(mockEffect); });
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
expect(mockEffect).toHaveBeenCalledTimes(1);
return [2 /*return*/];
}
});
}); });
it("should handle empty dependencies array vs undefined", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockEffect1, mockEffect2, rerender1, rerender2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockEffect1 = jest.fn().mockResolvedValue(undefined);
mockEffect2 = jest.fn().mockResolvedValue(undefined);
rerender1 = renderHook(function () {
return useAsyncEffekt(mockEffect1, []);
}).rerender;
rerender2 = renderHook(function () {
return useAsyncEffekt(mockEffect2);
}).rerender;
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 1:
_a.sent();
// Both should run once initially
expect(mockEffect1).toHaveBeenCalledTimes(1);
expect(mockEffect2).toHaveBeenCalledTimes(1);
// Rerender with empty deps should not re-run
rerender1();
// Rerender with undefined deps should re-run every time
rerender2();
return [4 /*yield*/, act(function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
jest.runAllTimers();
return [2 /*return*/];
});
}); })];
case 2:
_a.sent();
expect(mockEffect1).toHaveBeenCalledTimes(1); // Still 1 - empty deps
expect(mockEffect2).toHaveBeenCalledTimes(2); // Now 2 - undefined deps run on every render
return [2 /*return*/];
}
});
}); });
});