Skip to content

Commit 7e17c42

Browse files
authored
fix: infinite loop if effect schedules an update and then throws (#469)
Update lastValues before running the callback. Fixes #468
1 parent 7ac506f commit 7e17c42

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

.changeset/witty-zoos-peel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"haunted": patch
3+
---
4+
5+
Prevent infinite loops if effect schedules an update and then throws.

src/create-effect.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ function createEffect(setEffects: (state: State, cb: Callable) => void) {
2121
}
2222

2323
call(): void {
24-
if(!this.values || this.hasChanged()) {
24+
const hasChanged = !this.values || this.hasChanged();
25+
this.lastValues = this.values;
26+
27+
if(hasChanged) {
2528
this.run();
2629
}
27-
this.lastValues = this.values;
2830
}
2931

3032
run(): void {

test/use-effects.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,25 @@ describe('useEffect', () => {
176176
expect(parentEffects).to.equal(2);
177177
expect(childEffects).to.equal(2);
178178
});
179+
180+
it("Avoids causing infinite loops when the callback schedules an update, but then throws an exception", async () => {
181+
function App() {
182+
const [state, setState] = useState(0);
183+
184+
useEffect(() => {
185+
// an update is scheduled
186+
setState((state) => state! + 1);
187+
// an error is thrown
188+
throw new Error("Unexpected error");
189+
}, []);
190+
191+
return state;
192+
}
193+
194+
customElements.define("infinite-loop-test", component(App));
195+
196+
const el = await fixture(html`<infinite-loop-test></infinite-loop-test>`);
197+
expect(el).to.be.ok;
198+
expect(el.shadowRoot!.textContent).to.equal("1");
199+
});
179200
});

0 commit comments

Comments
 (0)