metal.js icon indicating copy to clipboard operation
metal.js copied to clipboard

Hard to check if a child component is a falsey value

Open bryceosterhaus opened this issue 8 years ago • 2 comments

When doing something like

<MyComponent>
  {false && <ChildComponent />}
</MyComponent>

~~You would expect that this.props.childreninMyComponentwould be[]`. Instead, the array contains one item.~~

this.props.children returns an array of Metal components even for a falsey value. It would be beneficial to have an easy way to check if the child was falsey.

jsfiddle example

bryceosterhaus avatar Aug 21 '17 21:08 bryceosterhaus

When rendering children, Metal still calls incrementalDom.elementVoid and passes it a function that increments the number of children of the current component. From what I remember this is done to optimize the reuse of dom elements when a render is triggered.

It's added to the props.children array here.

You could workaround it with something like...

function addChildCallToTree_(args, opt_isText) {
	const child = {
		parent: currentParent_,
		[CHILD_OWNER]: owner_
	};

	if (opt_isText) {
		child.text = args[0];
		if (args.length > 1) {
			child.args = args;
		}
	} else {
		child.tag = args[0];
		child.props = buildConfigFromCall(args);
		child.props.children = [];
		child.config = child.props;
	}

	if (typeof args[0] !== 'function' || args[0].name !== 'incElementCount') {
		addChildToTree(child);
	}

	return child;
}

But I'm not 100% sure if that won't cause issues with incremental dom's reuse of elements.

Edit: actually I'm pretty sure adding the child to props.children is what results in incElementCount getting invoked, so I think it would cause rendering performance issues.

robframpton avatar Aug 21 '17 22:08 robframpton

Looked into it a bit more and it seems okay that falsey values get registered as children. However, there is no easy way to check if the child is falsey since it gets turned into an instance of a metal component. I think the more appropriate change would probably be to not turn a falsey value into a metal component and rather have an easy way to check if that child was falsey within the parent component that is rendering the children.

React gets around this by returning children like [ReactElement, false, ReactElement, ReactElement]

Metal returns [MetalComponent, MetalComponent(falsey value), MetalComponent, MetalComponent]

bryceosterhaus avatar Aug 22 '17 17:08 bryceosterhaus