How to Programmatically render webcomponent as child of another component in VanillaJs? - javascript

I'm trying to learn vanilla webcomponents and I am stuck trying to do something simple and no combination of keywords I can think of return anything helpful in Google.
So in my render method, I see all sorts of examples that manually construct a tag and assign all the attribute, like so:
render()
{
this.innerHTML = `
${
this.data
.map(reason =>
`<${ReasonItem.elementName}
name="${reason.Name}"
description="${reason.Description}">
</${ReasonItem.elementName}>`)
.join('')
}
`;
}
This works, but it's extremely tedious for child controls that have LOTS of attributes.
I would like to do something like:
render()
{
this.innerHTML = `
${
this.data
.map(reason =>
{
let child = new ReasonItem();
child.reason = reason;
child.render();
return child.outerHTML;
})
.join('')
}
`;
This almost works, but apparently the constructor and other methods on an HTMLElement can be called out of order so I'm getting 'unidentified' in all my elements since the constructor calls render() and the setting of the property calls render(), but the constructors render is being called after the property is set, so I'm thinking I'm not doing this right.
Thank you for your time and attention.

since the constructor calls render()
That should never be done in the constructor. Leave that call to connectedCallback.
but the constructor's render is being called after the property is set
That's not possible unless you have an designed an async constructor.
Also, if you're not using Stencil or lit which do property binding and updating for you, you shouldn't try to mimic a render method to react to updates.
Instead, create your elements programmatically and have the component store references to those elements, so you can make targeted, partial updates later. Use getters and setters for the purpose.
Edit: Your solution code can be simplified:
render()
{
while(this.lastChild) this.lastChild.remove();
this.append(...this.data.map(reason => Object.assign(new ReasonItem, { reason })))
}
Also you probably need to empty your host element before rendering if you insist on a render() method.

#connexo So after lots of guess and testing, this appears to do what I need:
render()
{
this.data.forEach((reason) =>
{
const el = document.createElement(ReasonItem.elementName);
el.reason = reason;
this.appendChild(el);
});
}
I like the look of this better, not sure if it's equivalent or advisable, perhaps javascript pros can tell me what's 'generally preferred'?
render()
{
this.data.forEach((reason) =>
{
const el = new ReasonItem();
el.reason = reason;
this.appendChild(el);
});
}
Internally in my 'ReasonItem', the 'reason' property is observed and when changed, it loads (from the complex reason json object) all the dozens of attributes into their appropriate spots on the ReasonItem component.

Related

Class properties for react lifecycle methods

Can I write React lifecycle methods as class properties?
I've been using class properties for a while as I like the fact that I no longer have to manually bind my methods, but I'd like to keep some consistency across my components and I'm wondering if there is any drawback on writing the React lifecycle methods as class properties
import React, { Component } from 'react';
class MyComponent extends Component {
render = () => {
return (
<div>Foo Bar</div>
);
}
}
export default MyComponent;
For example, is the context of this class property affected compared to the context in an equivalent method. Given that the render method in the above code is written as an arrow function, this concern seems relevant.
In a way, the true answer depends on your build pipeline and what the resulting Javascript output looks like. There are two primary possibilities:
Input Code
Let's start by saying you are writing the following before going through any sort of pipeline transformations (babel, typescript, etc):
class Test {
test = () => { console.log('test'); };
}
Output as class member variable.
In one possible world, your pipeline will also be outputting the test function as a member variable for the output class. In this case the output might look something like:
function Test() {
this.test = function() { console.log('test'); };
}
This means that whenever you write new Test() the test function is going to be recreated every single time.
Output as class prototype function
In the other major possibility, your pipeline could be recognizing this as a function property and escape it from the class instance to the prototype. In this case the output might look something like:
function Test() {
}
Test.prototype = {
test: function() { console.log('test'); }
}
This means that no matter how many times you call new Test() there will still be only one creation of the test function around in memory.
Desired behavior
Hopefully it's clear that you want your end result to have the function end up on the prototype object rather than being recreated on each class instance.
However, while you would want the function to not end up as a property, that doesn't necessarily mean you couldn't write it that way in your own code. As long as your build chain is making the correct transformations, you can write it any way you prefer.
Although, looking at the default babel settings (which your babeljs tag leads me to believe you are using) it does not make this transformation for you. You can see this in action here. On the left I've created one class with the function as a property and one class with the function as a class method. On the right hand side, where babel shows it's output, you can see that the class with the function as a property still has it being an instance-level property, meaning it will be recreated each time that class's constructor is called.
I did find this babel plugin, which seems like it might add this transformation in, but I've not used it for myself so I'm not positive.
In my experience, the most reason for writing a method as a class property is when the method will be passed as a callback, and you need it to always be bound to the instance. React lifecycle methods will always be called as a method, so there's no reason to bind them (and you incur a tiny memory penalty when you do). Where this makes a difference is when you're passing a function to a component as a callback (e.g. onClick or onChange).
Take this example:
class BrokenFoo extends React.Component {
handleClick() {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
The function represented by this.handleClick is not automatically bound to the component instance, so when the method tries to read the value of this.props it will throw a TypeError because this is not defined. Read this article if you're not familiar with this; the problem described in section 4.2 "Pitfall: extracting methods improperly" is essentially what's happening when you pass around a method without making sure it's bound correctly.
Here's the class, rewritten with the handler as a class property:
class HappyFoo extends React.Component {
handleClick = () => {
alert(this.props.message);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
Effectively, you can think of the handleClick definition in the second example as placing this code into the component's constructor (which is just about exactly the way Babel does it):
this.handleClick = () => {
alert(this.props.message);
}
This achieves the same thing as calling bind on the function (as described in the linked article) but does it a little differently. Because this function is defined in the constructor, the value of this in this.props.message is bound to the containing instance. What this means is that the function is now independent of the calling context; you can pass it around and it won't break.
The rule of thumb that I follow: by default, write methods as methods. This attaches the method to the prototype and will usually behave the way you'd expect. However, if the method is ever written without parentheses (i.e. you're passing the value and not calling it), then you likely want to make it a class property.

JSX props should not use .bind() - how to avoid using bind?

I have a container that I need to change the UI form showing the form or showing a success page.
The container has a state.showSuccess and I need the MyFormModule to be able to call the container to change the state.
The below code works but I'm getting the following warning:
JSX props should not use .bind()
How can I get this to work without using .bind()?
...
const myPage = class extends React.Component {
state = { showSuccess: false };
showSuccess() {
this.setState({
showSuccess: true,
});
}
render() {
const { showSuccess } = this.state;
if (showSuccess) {...}
....
<MyFormModule showSuccess={this.showSuccess.bind(this)} />
You should first understand WHY this is a bad practice.
The main reason here, is that .bind is returning a new function reference.
This will happen on each render call, which may lead to a performance hit.
You got 2 options:
Use the constructor to bind your handlers (this will run only once).
constructor(props) {
super(props);
this.showSuccess = this.showSuccess.bind(this);
}
Or create your handlers with arrow functions so they will use the
lexical context for this, hence you won't need to bind them at
all (you will need a babel plugin):
showSuccess = () => {
this.setState({
showSuccess: true,
});
}
You should not use this pattern (as others suggested):
showSuccess={() => this.showSuccess()}
Because this will as well create a new function on each render.
So you may bypass the warning but you are still writing your code in a bad practice design.
From the ESLint docs:
A bind call or arrow function in a JSX prop will create a brand new
function on every single render. This is bad for performance, as it
will result in the garbage collector being invoked way more than is
necessary. It may also cause unnecessary re-renders if a brand new
function is passed as a prop to a component that uses reference
equality check on the prop to determine if it should update.
Use an arrow function when defining showSuccess
showSuccess = () => {
this.setState({
showSuccess: true,
});
}
Use an arrow function since they automatically inherit the this context of wherever they are defined.
showSuccess={() => this.showSuccess()}
Here is a link to the facebook documentation on this subject, which lists this method among others as a solution. Interestingly, they also list using .bind in the prop as one of the solutions, even though it produces a warning when actually used.
From that documentation, you'll note that this is a potential performance issue, since the function will be recreated on every render:
Note:
Using an arrow function in render creates a new function each time the
component renders, which may have performance implications (see
below).
But also from the same link:
Is it OK to use arrow functions in render methods? Generally speaking,
yes, it is OK, and it is often the easiest way to pass parameters to
callback functions.
If you do have performance issues, by all means, optimize!
So I would say if your component will be re-rendering very frequently, you should use one of the other solutions: bind in the constructor, or define the method with an arrow function in the first place. But if not, use whatever method seems cleanest to you.

referencing state in react

Keeping a reference to an specific (or "active") object is a pattern i use in many situations, to make changes to a specific object in an array of objects (or what have you).
state = {
object1: { 'data': 'data1'},
active_object: null,
}
// point active_object to object1
state.active_object = state.object1;
// modify it through the reference
state.active_object.data = 'data1modified';
// works as expected
console.log('object1.data: ' + state.object1.data);
Now the equivalent in a react component state would be something along theses lines (note that this is NON working tho). I suppose the reference gets lost "somewhere in translation". Is there a (simple) way to mimic the pattern above (other than restructuring, using object keys and such) to accomplish the same thing using react and state?
class App extends Component {
constructor() {
super()
this.state = {
active_object: null,
object1: { 'data': 'data1'},
}
}
modifyState() {
// point active_object to object1 (using *functional* setState)
this.setState((state) => (
{ active_object: state.object1 }
))
// modify it through the reference (using *functional* setState)
this.setState((state) => (
{ active_object: { ...state.active_object, ['data']: 'data1modified' } }
))
}
render() {
return (
<div className="App">
<p onClick={ () => this.modifyState() }>modify</p>
<p>{ this.state.object1.data }</p>
</div>
);
}
}
EDIT: i've updated the example to use a constructor to not confuse people (i'm using babel, so not really needed in reality). Also, i've indicated more clearly in the comments that i'm using functional setState (also, to no confuse people). I need to clarify that i don't need help with getting this particular piece of code working, it's more about demonstrating what i would like to be able to do. In that regard, it pseudo-code even tho it actually works fine if you run it in react (other than that the reference gets lost, as expected).
EDIT2: As vs1682 point out in the comments to this question, the reference gets lost by using the objects spread operator when copying the object. So this problem isn't really react specific (only indirectly).
Maybe this is an egregious typo, but you don't have a constructor. If you don't understand why you need one, maybe you should go back to basics and read up a bit on JavaScript ES6.
constructor(props) {
super(props)
this.state = {
active_object: null,
object1: { 'data': 'data1'},
}
}
this.setState does not accept a callback as its main argument... or at least this is not convention. this.setState can accept a callback as a secondary argument, but that is to handle the async nature of this.setState. It is not explicitly for changing state.
this.setState({ active_object: this.state.object1 })
Also, JavaScript convention highly encourages camelCase, not underscores.
edit: I was wrong. A function can be used in this.setState. In fact, it can be a pretty good idea.
Turns out you that if you have references to properties within the same object (such as in the case in the question), you can perform a deep copy with for example clone. That will "relink" (i'm unsure of the terminology here) references even though a new object is returned with fresh content.
The only downside i see with this approach is that references that reach outside of the object you're cloning will not be preserved, whereas in a shallow copy (what you have with object spread or immutability-helper) it will, since it doesn't modify references at all. This specific gotcha might be related to clone though.
Anyway, this works, but i have no idea of the performance implications, or if it even is good practice considering the functional nature of react and state.
import React, { Component } from 'react';
import clone from 'clone';
class App extends Component {
constructor() {
super()
this.state = {
activeObject: null,
object1: { 'data': 'data1'},
}
}
modifyState() {
// point activeObject to object1
this.setState((state) => {
let newState = clone(state);
newState.activeObject = newState.object1;
return newState;
})
// modify it through the reference
this.setState((state) => {
let newState = clone(state);
newState.activeObject.data = 'data1-modified';
return newState;
})
}
render() {
return (
<div className="App">
<p onClick={ () => this.modifyState() }>modify</p>
{/* will show 'data1-modified' after modifyState */}
<p>{ this.state.object1.data }</p>
</div>
);
}
}
It should probably be said that cloning objects with references is always tricky business, and requires more often then not that you set up a special method (specific for copying your object or data structure) than relying on shallow/deep copying alone. In my case, i think a "normal" shallow copy and a handler to restore references would be the best way to go.
Or, skip the reference altogether, and rely on unique object properties (such as and "active" identifier, or an "id") to replace the troublesome reference in this scenario. I suspect that in languages that rely on copy by value (and immutability) where the react model of immutable state comes from, this is less of a problem because of the lack (not always, but often) of object/value references.

React DnD: Avoid using findDOMNode

I don't fully understand it but apparently it isn't recommended to use findDOMNode().
I'm trying to create drag and drop component but I'm not sure how I should access refs from the component variable. This is an example of what I currently have:
const cardTarget = {
hover(props, monitor, component) {
...
// Determine rectangle on screen
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
...
}
}
Source
Edit
It might be caused by my component being both the drag and drop source and target as I can get it to work in this example but not this one.
Assuming you're using es6 class syntax and the most recent version of React (15, at time of writing), you can attach a callback ref like Dan did in his example on the link you shared. From the docs:
When the ref attribute is used on an HTML element, the ref callback receives the underlying DOM element as its argument. For example, this code uses the ref callback to store a reference to a DOM node:
<h3
className="widget"
onMouseOver={ this.handleHover.bind( this ) }
ref={node => this.node = node}
>
Then you can access the node just like we used to do with our old friends findDOMNode() or getDOMNode():
handleHover() {
const rect = this.node.getBoundingClientRect(); // Your DOM node
this.setState({ rect });
}
In action:
https://jsfiddle.net/ftub8ro6/
Edit:
Because React DND does a bit of magic behind the scenes, we have to use their API to get at the decorated component. They provide getDecoratedComponentInstance() so you can get at the underlying component. Once you use that, you can get the component.node as expected:
hover(props, monitor, component) {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
const rawComponent = component.getDecoratedComponentInstance();
console.log( rawComponent.node.getBoundingClientRect() );
...
Here it is in action:
https://jsfiddle.net/h4w4btz9/2/
Better Solution
A better solution is to just wrap your draggable component with a div, define a ref on that and pass it to the draggable component, i.e.
<div key={key} ref={node => { this.node = node; }}>
<MyComponent
node={this.node}
/>
</div>
and MyComponent is wrapped in DragSource. Now you can just use
hover(props, monitor, component) {
...
props.node && props.node.getBoundingClientRect();
...
}
(props.node && is just added to avoid to call getBoundingClientRect on an undefined object)
Alternative for findDOMNode
If you don't want to add a wrapping div, you could do the following.
The reply of #imjared and the suggested solution here don't work (at least in react-dnd#2.3.0 and react#15.3.1).
The only working alternative for findDOMNode(component).getBoundingClientRect(); which does not use findDOMNode is:
hover(props, monitor, component) {
...
component.decoratedComponentInstance._reactInternalInstance._renderedComponent._hostNode.getBoundingClientRect();
...
}
which is not very beautiful and dangerous because react could change this internal path in future versions!
Other (weaker) Alternative
Use monitor.getDifferenceFromInitialOffset(); which will not give you precise values, but is perhaps good enough in case you have a small dragSource. Then the returned value is pretty predictable with a small error margin depending on the size of your dragSource.
React-DnD's API is super flexible—we can (ab)use this.
For example, React-DnD lets us determine what connectors are passed to the underlying component. Which means we can wrap them, too. :)
For example, let's override the target connector to store the node on the monitor. We will use a Symbol so we do not leak this little hack to the outside world.
const NODE = Symbol('Node')
function targetCollector(connect, monitor) {
const connectDropTarget = connect.dropTarget()
return {
// Consumer does not have to know what we're doing ;)
connectDropTarget: node => {
monitor[NODE] = node
connectDropTarget(node)
}
}
}
Now in your hover method, you can use
const node = monitor[NODE]
const hoverBoundingRect = node.getBoundingClientRect()
This approach piggybacks on React-DnD's flow and shields the outside world by using a Symbol.
Whether you're using this approach or the class-based this.node = node ref approach, you're relying on the underlying React node. I prefer this one because the consumer does not have to remember to manually use a ref other than the ones already required by React-DnD, and the consumer does not have to be a class component either.

React, ineffiencies of binding a new function for each event

My friend and I are having an argument. In the interest of full disclosure, I'm the one who's a big fan of React and its benefits.
In React components, when attaching a DOM event to each element in a list of elements, the traditional pattern is to bind() the generic click handler with the values you want to pass along to that function as parameters. As written below:
<button onClick={this.onButtonClick.bind(this, buttonIndex)}></button>
where buttonIndex is some value that changes as the list of buttons is iterated over. This pattern allows onButtonClick to be generic, and expect buttonIndex as a parameter. Like so:
onButtonClick: function(buttonIndex) {
... some logic
}
My friend argues that this way of doing things is extremely inefficient. This requires that a new function be created and kept in memory to handle each button's event. I agree with his point, but I feel that the React devs wouldn't encourage this pattern in their docs, (at least twice) if the library didn't efficiently handle the events and their handlers.
The pattern he used to avoid this was to use the data- attribute and get the value (in this example, buttonIndex) off the DOM element itself:
<button data-buttonIndex={buttonIndex} onClick={this.onButtonClick}></button>
...
onButtonClick: function() {
var buttonIndex = $(this).data('buttonIndex');
...some logic
}
Again, I'm biased cus I'm the React fan. But this feels wrong, for two reasons:
Getting values off the DOM to pass data around (as state) kinda defeats the purpose of React in a lot of ways, right?
data- attributes are extremely ambiguous in my opinion. They can be set from several different places (HTML, JS, PHP, etc.). And they don't suggest any implicit purpose. That "data" could be used anywhere, (JS, CSS, etc.)
Does React do some special magic to be efficent with its DOM events? And if not, is there an alternative pattern that doesn't use the data- attribute and is more explicit about its use?
I think in general binding functions directly in render is the idiomatic way because they do it in the docs as you pointed out and in our experience has not been significantly less performant. However, there are cases you don't want to rebind the function on every render, for example if you're comparing props in shouldComponentUpdate (like with PureRenderMixin). To do something very similar as your friend suggests but without looking at the DOM with jQuery (and I believe is a common pattern) you can pass the index as a prop.
class Parent extends React.Component {
render() {
return [...some array].map((item, index) => {
return <Child item={item} index={index} />;
});
}
}
class Child extends React.Component {
constructor() {
super();
this.handleClickButton = this.handleClickButton.bind(this);
}
handleClickButton() {
// use this.props.index here
}
render() {
return <button onClick={this.handleClickButton}></button>;
}
}
Note when using ES6 classes you need to bind to this manually in constructor since you're accessing this.props. You don't need to if you're using React.createClass. More about ES6 classes in React docs here.
I'm not sure this is a good idea, but... memoize!
class Foo {
constructor(){
this.getClickHandler = _.memoize(this.getClickHandler);
}
getClickHandler(index){
return (event) => {
doSomething();
};
}
render(){
// ...
<button onClick={this.getClickHandler(index)}>Click me</button>
// ...
}
}
This way you avoid creating a new function, avoid data-attributes, and avoid the performance cost of looking up anything in the dom.
I don't think I've ever profiled and found creating functions in render to be an issue. This is definitely something you should optimize only when the numbers tell you to do so.

Categories

Resources