I have the following hyperHTML component, everything is working as intended, the only issue is that the entire component DOM re renders on every this.setState()
call.
My question:
Is there a way to update the notification string on the DOM without re rendering the entire component DOM ?
const { hyper } = hyperHTML;
class searchComponent extends hyper.Component {
constructor(data) {
super();
this.setState({ notification: '' });
}
onChange() {
// ...
if ( condition ) {
this.setState(() => ({ notification: 'notification text!' }));
}
// ...
}
async listTags() {
//...
// async content
//...
}
render() {
this.html `
<div id="search_container">
<select name="tags" id="tags_search" class='form-control m0' multiple="multiple" onChange=${this.onChange}>
${{
any: this.listTags(),
placeholder: 'incoming..!',
}}
</select>
<div class="search_notification">
<!-- I want to re render only this part of the DOM -->
${this.state.notification}
</div>
</div>
`
}
}
There are few gotchas in your code. To start with, that
onChange
won't work as expected, you are losing the context as it is. You either just useonchange
HTML event, and you call the methodonchange
and you use simplythis
as event listener:or you bind the
onChange
method within the constructor.You also forgot to
return this.html
insiderender()
, which is something needed if you want to place your component on the DOM.However, assuming these were irrelevant errors there for quick demo code sake, you need to understand that async world is async.
If you have an asynchronous operation there's no way you can tell if that resolved or not, you just pass it along, and this is exactly what you are doing.
You are changing the state of the component expecting that previously async operations would not be triggered again.
But how can the component know if such state change would produce a different layout? The TL;DR answer is that it cannot, the slightly longer one is that you have an asynchronous, unrelated, behavior, attached to each updates but you want that asynchronous behavior to be triggered only once, and not per update.
Possible solutions
You can either create a sub component a part, and instantiate it once during the
SearchComponent
initialization (constructor
) or simply use a placeholder that makes more sense than the current one.Indeed, right now you are creating output like
<select>incoming..!</select>
which makes no sense as standard HTML.A
select
can haveoption
tags, using it as a generic container of some text is like abusing its potentials.What you want is something like a
disabled=${this.optionsResolved}
on theselect
, and${this.options}
as array of its content.In this way you have no changes whatsoever while resolving your options, and a proper list of options once that happens.
You can see an example in this Code Pen