Skip to content

Commit

Permalink
fix case where parent catches error and switches vnode return type (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed Oct 31, 2023
1 parent 66cb6a7 commit 4cad961
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 2 deletions.
81 changes: 79 additions & 2 deletions hooks/test/browser/errorBoundary.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createElement, render } from 'preact';
import { Fragment, createElement, render } from 'preact';
import { setupScratch, teardown } from '../../../test/_util/helpers';
import { useErrorBoundary, useLayoutEffect } from 'preact/hooks';
import { useErrorBoundary, useLayoutEffect, useState } from 'preact/hooks';
import { setupRerender } from 'preact/test-utils';

/** @jsx createElement */
Expand Down Expand Up @@ -152,4 +152,81 @@ describe('errorBoundary', () => {
expect(badEffect).to.be.calledOnce;
expect(goodEffect).to.be.calledOnce;
});

it('should not duplicate in lists where an item throws and the parent catches and returns a differing type', () => {
const baseTodos = [
{ text: 'first item', completed: false },
{ text: 'Test the feature', completed: false },
{ text: 'another item', completed: false }
];

function TodoList() {
const [todos, setTodos] = useState([...baseTodos]);

return (
<Fragment>
<ul>
{todos.map((todo, index) => (
<TodoItem
key={index}
toggleTodo={() => {
todos[index] = {
...todos[index],
completed: !todos[index].completed
};
setTodos([...todos]);
}}
todo={todo}
index={index}
/>
))}
</ul>
</Fragment>
);
}

function TodoItem(props) {
const [error] = useErrorBoundary();

if (error) {
return <li>An error occurred: {error}</li>;
}

return <TodoItemInner {...props} />;
}
let set;
function TodoItemInner({ todo, index, toggleTodo }) {
if (todo.completed) {
throw new Error('Todo completed!');
}

if (index === 1) {
set = toggleTodo;
}

return (
<li>
<label>
<input
type="checkbox"
checked={todo.completed}
onInput={toggleTodo}
/>
{todo.completed ? <s>{todo.text}</s> : todo.text}
</label>
</li>
);
}

render(<TodoList />, scratch);
expect(scratch.innerHTML).to.equal(
'<ul><li><label><input type="checkbox">first item</label></li><li><label><input type="checkbox">Test the feature</label></li><li><label><input type="checkbox">another item</label></li></ul>'
);

set();
rerender();
expect(scratch.innerHTML).to.equal(
'<ul><li><label><input type="checkbox">first item</label></li><li>An error occurred: </li><li><label><input type="checkbox">another item</label></li></ul>'
);
});
});
3 changes: 3 additions & 0 deletions src/diff/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ export function diff(
excessDomChildren[excessDomChildren.indexOf(oldDom)] = null;
// ^ could possibly be simplified to:
// excessDomChildren.length = 0;
} else {
newVNode._dom = oldVNode._dom;
newVNode._children = oldVNode._children;
}
options._catchError(e, newVNode, oldVNode);
}
Expand Down

0 comments on commit 4cad961

Please sign in to comment.