Reputation: 1056
Okay I realize this going to be a super long example, but I am thoroughly baffled by this, so thought I'd both share it with everyone and also try to find a solution.
So I'm trying to make a "tag input" component where you type into it, and whenever you type space it appends that string to a list you pass in. In order to remove one of the "tags", you clear out the content editable area, hit backspace once to "prep" the last tag in the list for removal, and then again to confirm the removal. It makes sense in context, but I've written a stripped down version for the sake of the example. I have the following codesandbox: https://codesandbox.io/s/8q0q3qw60
Now, here's the part that I don't get.
Everything seems to be working entirely as intended, except for the actual removal of the tag. For some reason, I can appropriately "prep" the last tag for removal, however when I click backspace again to confirm, for some reason the state (from a hook) for prepTagForRemoval
within the closure that is the keyDown callback for the content editable area never changes. It is always false, but only within the callback! This results in the tag never being deleted appropriately after confirming its deletion.
In order to repro this...
Currently, at this point, it should have just deleted "hello" from the "Tags:" row, however the actual behavior is that it simply set prepForRemoval to false, and turns hello black again, without removing "hello" from "Tags:"
Sorry if this is confusing, I'm happy to try to clarify more. I really want to get this example properly working and removing the last tag (or at least calling the onRemove) when the second delete is emitted. Hope someone can lend a hand!
Upvotes: 2
Views: 557
Reputation: 81006
This is a bug in react-contenteditable. Below is its shouldComponentUpdate
method. It isn't checking for a change to onKeyDown
and since the backspace won't cause any change to the value, it won't re-render so it is using a stale version of your handleKeyDown
function.
shouldComponentUpdate(nextProps: Props): boolean {
const { props } = this;
const el = this.getEl();
// We need not rerender if the change of props simply reflects the user's edits.
// Rerendering in this case would make the cursor/caret jump
// Rerender if there is no element yet... (somehow?)
if (!el) return true;
// ...or if html really changed... (programmatically, not by user edit)
if (
normalizeHtml(nextProps.html) !== normalizeHtml(el.innerHTML)
) {
return true;
}
// Handle additional properties
return props.disabled !== nextProps.disabled ||
props.tagName !== nextProps.tagName ||
props.className !== nextProps.className ||
props.innerRef !== nextProps.innerRef ||
!deepEqual(props.style, nextProps.style);
}
Here it is working with a fixed copy of react-contenteditable: https://codesandbox.io/s/o41yjr3r3q
I changed the last part of shouldComponentUpdate
to add props.onKeyDown !== nextProps.onKeyDown
:
return (
props.disabled !== nextProps.disabled ||
props.tagName !== nextProps.tagName ||
props.className !== nextProps.className ||
props.innerRef !== nextProps.innerRef ||
props.onKeyDown !== nextProps.onKeyDown ||
!deepEqual(props.style, nextProps.style)
);
Upvotes: 2