Skip to content

Commit

Permalink
- Added Children APIs, sValidElement and cloneElement API (unstable r…
Browse files Browse the repository at this point in the history
…ight now)

- Support multi param children in createElement.
- Clean effect before commit and not before render.
- Fixed regression bug with context API
  • Loading branch information
s-yadav committed Aug 24, 2020
1 parent 96ebd39 commit bb62697
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 21 deletions.
92 changes: 92 additions & 0 deletions src/Children.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { isTagElementNode, isComponentNode, isTagNode } from './brahmosNode';

const { isNil } = require('./utils');

function isPlaceholderTagNode(children) {
/**
* if the template string for a tag node does not have any thing,
* it means the tag node is just used to wrap children.
* Note: We always wrap the children except when children is already an array
* or there is single child. We do this to make the all the children positional
* */
return isTagNode(children) && !children.template.strings.some(Boolean);
}

function getChildrenArray(children) {
if (isNil(children)) return undefined;
else if (typeof children === 'boolean') return [];

// if children is a tag node and is just a placeholder for all the children use the values from the node
if (isPlaceholderTagNode(children)) {
children = children.values;
}

if (!Array.isArray(children)) children = [children];

return children;
}

function map(children, cb) {
const _children = getChildrenArray(children);
if (!_children) return children;
const newChildren = _children.map(cb);

/**
* if we got a placeholder tag node, after map,
* we should return cloned placeholder tag nodes with updated values
*/
if (isPlaceholderTagNode(children)) {
return { ...children, values: newChildren };
}

return newChildren;
}

function toArray(children) {
const _children = getChildrenArray(children) || [];

return _children.map((child, index) => {
/**
* As we have converted children to a flat array node
* assign key to each elements if it isn't already present
*/
if (child.key === undefined) {
child.key = index;
}
return child;
});
}

function forEach(children, cb) {
const _children = getChildrenArray(children) || [];
_children.forEach(cb);
}

function only(children) {
const _children = getChildrenArray(children);
return _children && children.length === 1;
}

export const Children = {
map,
toArray,
forEach,
only,
};

export function isValidElement(node) {
return node && (isComponentNode(node) || isTagElementNode(node));
}

export function cloneElement(node, props, children) {
if (node) {
if (isTagElementNode(node)) {
const newProps = { ...node.values[0], ...props };
return { ...node, values: [newProps, children] };
} else if (isComponentNode(node)) {
return { ...node, props: { ...node.props, ...props, children } };
}
}

return node;
}
4 changes: 4 additions & 0 deletions src/brahmosNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export const CLASS_COMPONENT_NODE = Symbol('class-component');
export const FUNCTIONAL_COMPONENT_NODE = Symbol('functional-component');
export const ATTRIBUTE_NODE = Symbol('attribute');

export function isTagElementNode({ nodeType }) {
return nodeType === TAG_ELEMENT_NODE;
}

export function isTagNode({ nodeType }) {
return nodeType === TAG_NODE || nodeType === TAG_ELEMENT_NODE;
}
Expand Down
13 changes: 10 additions & 3 deletions src/createElement.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getNormalizedProps } from './utils';
import { getNormalizedProps, toArray } from './utils';
import { isClassComponent } from './circularDep';
import {
brahmosNode,
Expand All @@ -22,17 +22,24 @@ export function createTagElement(element, configs, children) {
}

export function createElement(element, configs, children) {
configs = configs || {};
/**
* If there is single children no need to keep it as an array
*/
const argLn = arguments.length;
const _children = argLn > 3 ? toArray(arguments, 2, argLn) : children;

/**
* If the create element is receiving an string element it means it is not a component,
* but a simple tag instead. In that case return a tagElement instance.
*/
if (typeof element === 'string') return createTagElement(element, configs, children);
if (typeof element === 'string') return createTagElement(element, configs, _children);

// create a prop object excluding the key and ref prop and adding children prop
const props = {
...element.defaultProps,
...getNormalizedProps(configs, element.__isForwardRef),
children,
children: _children,
};

// if the element is a lazy component, start fetching the underlying component
Expand Down
5 changes: 4 additions & 1 deletion src/effectLoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import { callLifeCycle, insertBefore, getNextSibling } from './utils';
import { getTransitionFromFiber } from './transitionUtils';
import { getPendingUpdatesKey } from './updateUtils';
import { runEffects } from './hooks';
import { runEffects, cleanEffects } from './hooks';

import updateNodeAttributes from './updateAttribute';
import {
Expand Down Expand Up @@ -145,6 +145,9 @@ function handleComponentEffect(fiber) {
prevProps,
prevState,
]);
} else {
// clean the existing effect
cleanEffects(fiber);
}

// remove all the pending updates associated with current transition
Expand Down
9 changes: 9 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ import unmountComponentAtNode from './unmountComponentAtNode';
/** unstableBatchedUpdate */
import { deferredUpdates, syncUpdates } from './updateUtils';

/** import top level api */
import { Children, isValidElement, cloneElement } from './Children';

const Brahmos = {
createElement,
render,
Expand All @@ -67,6 +70,9 @@ const Brahmos = {
Suspense,
SuspenseList,
lazy,
Children,
isValidElement,
cloneElement,
unstable_deferredUpdates: deferredUpdates,
unstable_syncUpdates: syncUpdates,
};
Expand Down Expand Up @@ -95,6 +101,9 @@ export {
Suspense,
SuspenseList,
lazy,
Children,
isValidElement,
cloneElement,
deferredUpdates as unstable_deferredUpdates,
syncUpdates as unstable_syncUpdates,
};
Expand Down
16 changes: 1 addition & 15 deletions src/processComponentFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ function getCurrentContext(fiber) {
node: { type: Component },
nodeInstance,
parent,
context: currentContext,
} = fiber;

// if component has createContext index, we treat it as provider
Expand All @@ -64,15 +63,7 @@ function getCurrentContext(fiber) {
// if component is not a provider return the same context
if (!__ccId) return context;

/**
* if there is a context on fiber node and the nodeInstance
* is reused we can always return that context,
* if provider in parent hierarchy is changed, the whole child hierarchy will
* be different and nodeInstance are not reused.
*/
if (currentContext) return currentContext;

// for new provider instance create a new context extending the parent context
// for new provider instance create a new context by extending the parent context
const newContext = Object.create(context);

// store the nodeInstance
Expand Down Expand Up @@ -238,11 +229,6 @@ export default function processComponentFiber(fiber) {
transitionId: currentTransition.transitionId,
};
}
} else if (!isFirstRender) {
// for functional component call cleanEffect only on second render
// alternate will be set on second render
// NOTE: This is buggy, cleanEffects should be called before commit phase, check the behavior of react.
cleanEffects(fiber);
}

// render the nodes
Expand Down
5 changes: 3 additions & 2 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ export function remove(nodes) {
* Convert an array like object to array
*/

export function toArray(list) {
return Array.prototype.slice.call(list);
export function toArray(list, start) {
start = start || 0;
return Array.prototype.slice.call(list, start);
}

/**
Expand Down

0 comments on commit bb62697

Please sign in to comment.