Skip to content

Commit

Permalink
fix: infinite loop if effect schedules an update and then throws (#469)
Browse files Browse the repository at this point in the history
Update lastValues before running the callback.

Fixes #468
  • Loading branch information
cristinecula authored Oct 22, 2024
1 parent 7ac506f commit 7e17c42
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/witty-zoos-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"haunted": patch
---

Prevent infinite loops if effect schedules an update and then throws.
6 changes: 4 additions & 2 deletions src/create-effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ function createEffect(setEffects: (state: State, cb: Callable) => void) {
}

call(): void {
if(!this.values || this.hasChanged()) {
const hasChanged = !this.values || this.hasChanged();
this.lastValues = this.values;

if(hasChanged) {
this.run();
}
this.lastValues = this.values;
}

run(): void {
Expand Down
21 changes: 21 additions & 0 deletions test/use-effects.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,25 @@ describe('useEffect', () => {
expect(parentEffects).to.equal(2);
expect(childEffects).to.equal(2);
});

it("Avoids causing infinite loops when the callback schedules an update, but then throws an exception", async () => {
function App() {
const [state, setState] = useState(0);

useEffect(() => {
// an update is scheduled
setState((state) => state! + 1);
// an error is thrown
throw new Error("Unexpected error");
}, []);

return state;
}

customElements.define("infinite-loop-test", component(App));

const el = await fixture(html`<infinite-loop-test></infinite-loop-test>`);
expect(el).to.be.ok;
expect(el.shadowRoot!.textContent).to.equal("1");
});
});

0 comments on commit 7e17c42

Please sign in to comment.