Skip to content

Commit

Permalink
Change internal VNode data structure. Close #578.
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgebucaran committed Feb 7, 2018
1 parent 1b752a7 commit 4a783a3
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 61 deletions.
67 changes: 35 additions & 32 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function h(name, props /*, ...rest*/) {
export function h(name, attributes /*, ...rest*/) {
var node
var rest = []
var children = []
Expand All @@ -17,10 +17,10 @@ export function h(name, props /*, ...rest*/) {
}

return typeof name === "function"
? name(props || {}, children)
? name(attributes || {}, children)
: {
name: name,
props: props || {},
nodeName: name,
attributes: attributes || {},
children: children
}
}
Expand All @@ -39,8 +39,8 @@ export function app(state, actions, view, container) {

function toVNode(element, map) {
return {
name: element.nodeName.toLowerCase(),
props: {},
nodeName: element.nodeName.toLowerCase(),
attributes: {},
children: map.call(element.childNodes, function(element) {
return element.nodeType === 3 // Node.TEXT_NODE
? element.nodeValue
Expand Down Expand Up @@ -124,7 +124,7 @@ export function app(state, actions, view, container) {
}

function getKey(node) {
return node && node.props ? node.props.key : null
return node && node.attributes ? node.attributes.key : null
}

function setElementProp(element, name, value, isSVG, oldValue) {
Expand All @@ -150,56 +150,59 @@ export function app(state, actions, view, container) {
var element =
typeof node === "string" || typeof node === "number"
? document.createTextNode(node)
: (isSVG = isSVG || node.name === "svg")
? document.createElementNS("http://www.w3.org/2000/svg", node.name)
: document.createElement(node.name)
: (isSVG = isSVG || node.nodeName === "svg")
? document.createElementNS(
"http://www.w3.org/2000/svg",
node.nodeName
)
: document.createElement(node.nodeName)

if (node.props) {
if (node.props.oncreate) {
if (node.attributes) {
if (node.attributes.oncreate) {
invokeLaterStack.push(function() {
node.props.oncreate(element)
node.attributes.oncreate(element)
})
}

for (var i = 0; i < node.children.length; i++) {
element.appendChild(createElement(node.children[i], isSVG))
}

for (var name in node.props) {
setElementProp(element, name, node.props[name], isSVG)
for (var name in node.attributes) {
setElementProp(element, name, node.attributes[name], isSVG)
}
}

return element
}

function updateElement(element, oldProps, props, isSVG) {
for (var name in copy(oldProps, props)) {
function updateElement(element, oldProps, attributes, isSVG) {
for (var name in copy(oldProps, attributes)) {
if (
props[name] !==
attributes[name] !==
(name === "value" || name === "checked"
? element[name]
: oldProps[name])
) {
setElementProp(element, name, props[name], isSVG, oldProps[name])
setElementProp(element, name, attributes[name], isSVG, oldProps[name])
}
}

if (props.onupdate) {
if (attributes.onupdate) {
invokeLaterStack.push(function() {
props.onupdate(element, oldProps)
attributes.onupdate(element, oldProps)
})
}
}

function removeChildren(element, node, props) {
if ((props = node.props)) {
function removeChildren(element, node, attributes) {
if ((attributes = node.attributes)) {
for (var i = 0; i < node.children.length; i++) {
removeChildren(element.childNodes[i], node.children[i])
}

if (props.ondestroy) {
props.ondestroy(element)
if (attributes.ondestroy) {
attributes.ondestroy(element)
}
}
return element
Expand All @@ -210,7 +213,7 @@ export function app(state, actions, view, container) {
parent.removeChild(removeChildren(element, node))
}

if (node.props && (cb = node.props.onremove)) {
if (node.attributes && (cb = node.attributes.onremove)) {
cb(element, done)
} else {
done()
Expand All @@ -221,12 +224,12 @@ export function app(state, actions, view, container) {
if (node === oldNode) {
} else if (oldNode == null) {
element = parent.insertBefore(createElement(node, isSVG), element)
} else if (node.name && node.name === oldNode.name) {
} else if (node.nodeName && node.nodeName === oldNode.nodeName) {
updateElement(
element,
oldNode.props,
node.props,
(isSVG = isSVG || node.name === "svg")
oldNode.attributes,
node.attributes,
(isSVG = isSVG || node.nodeName === "svg")
)

var oldElements = []
Expand Down Expand Up @@ -297,11 +300,11 @@ export function app(state, actions, view, container) {
}

for (var i in oldKeyed) {
if (!newKeyed[oldKeyed[i][1].props.key]) {
if (!newKeyed[oldKeyed[i][1].attributes.key]) {
removeElement(element, oldKeyed[i][0], oldKeyed[i][1])
}
}
} else if (node.name === oldNode.name) {
} else if (node.nodeName === oldNode.nodeName) {
element.nodeValue = node
} else {
element = parent.insertBefore(
Expand Down
58 changes: 29 additions & 29 deletions test/h.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,47 @@ import { h } from "../src"

test("empty vnode", () => {
expect(h("div")).toEqual({
name: "div",
props: {},
nodeName: "div",
attributes: {},
children: []
})
})

test("vnode with a single child", () => {
expect(h("div", {}, ["foo"])).toEqual({
name: "div",
props: {},
nodeName: "div",
attributes: {},
children: ["foo"]
})

expect(h("div", {}, "foo")).toEqual({
name: "div",
props: {},
nodeName: "div",
attributes: {},
children: ["foo"]
})
})

test("positional String/Number children", () => {
expect(h("div", {}, "foo", "bar", "baz")).toEqual({
name: "div",
props: {},
nodeName: "div",
attributes: {},
children: ["foo", "bar", "baz"]
})

expect(h("div", {}, 0, "foo", 1, "baz", 2)).toEqual({
name: "div",
props: {},
nodeName: "div",
attributes: {},
children: [0, "foo", 1, "baz", 2]
})

expect(h("div", {}, "foo", h("div", {}, "bar"), "baz", "quux")).toEqual({
name: "div",
props: {},
nodeName: "div",
attributes: {},
children: [
"foo",
{
name: "div",
props: {},
nodeName: "div",
attributes: {},
children: ["bar"]
},
"baz",
Expand All @@ -51,26 +51,26 @@ test("positional String/Number children", () => {
})
})

test("vnode with props", () => {
const props = {
test("vnode with attributes", () => {
const attributes = {
id: "foo",
class: "bar",
style: {
color: "red"
}
}

expect(h("div", props, "baz")).toEqual({
name: "div",
props,
expect(h("div", attributes, "baz")).toEqual({
nodeName: "div",
attributes,
children: ["baz"]
})
})

test("skip null and Boolean children", () => {
const expected = {
name: "div",
props: {},
nodeName: "div",
attributes: {},
children: []
}

Expand All @@ -83,18 +83,18 @@ test("components", () => {
const Component = (props, children) => h("div", props, children)

expect(h(Component, { id: "foo" }, "bar")).toEqual({
name: "div",
props: { id: "foo" },
nodeName: "div",
attributes: { id: "foo" },
children: ["bar"]
})

expect(h(Component, { id: "foo" }, [h(Component, { id: "bar" })])).toEqual({
name: "div",
props: { id: "foo" },
nodeName: "div",
attributes: { id: "foo" },
children: [
{
name: "div",
props: { id: "bar" },
nodeName: "div",
attributes: { id: "bar" },
children: []
}
]
Expand All @@ -106,8 +106,8 @@ test("component with no props adds default props", () => {
h("div", {}, "Hello " + name)

expect(h(Component)).toEqual({
name: "div",
props: {},
nodeName: "div",
attributes: {},
children: ["Hello world"]
})
})

0 comments on commit 4a783a3

Please sign in to comment.