Skip to content

Commit

Permalink
Merge pull request #86 from omochi/vnode-disc
Browse files Browse the repository at this point in the history
VNodeDiscriminator
  • Loading branch information
omochi committed Apr 24, 2024
2 parents c00fbbb + b4f1a48 commit b8667da
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 46 deletions.
30 changes: 15 additions & 15 deletions Sources/React/Renderer/ReactRoot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -382,42 +382,42 @@ public final class ReactRoot {
) throws {
var patchedOldChildren: [VNode?] = oldChildren

let diff = newChildren.difference(from: oldChildren)
.inferringMoves()
let diff = VNode.match(newChildren: newChildren, oldChildren: oldChildren)

var nextIndex = 0

for patch in diff {
switch patch {
case .remove(offset: let offset, element: let oldNode, associatedWith: let dest):
case .remove(offset: let offset, element: let old, associatedWith: let assoc):
patchedOldChildren.remove(at: offset)

if let _ = dest {
if assoc != nil {
// process on insert
} else {
try renderNode(new: nil, old: oldNode)
try renderNode(new: nil, old: old)
}
case .insert(offset: let offset, element: let newNode, associatedWith: let source):
for index in nextIndex..<offset {
let newNode = newChildren[index]
let oldNode = try patchedOldChildren[index].unwrap("updating oldNode")
case .insert(offset: let offset, element: let newNode, associatedWith: let assoc):
while nextIndex < offset {
let newNode = newChildren[nextIndex]
let oldNode = try patchedOldChildren[nextIndex].unwrap("updating oldNode")
try renderNode(new: newNode, old: oldNode)
nextIndex += 1
}
nextIndex = offset + 1

patchedOldChildren.insert(nil, at: offset)

let oldNode = source.map { oldChildren[$0] }
let oldNode = assoc.map { oldChildren[$0] }
try renderNode(new: newNode, old: oldNode, isMove: oldNode != nil)
nextIndex += 1
}
}

for index in nextIndex..<newChildren.count {
let newNode = newChildren[index]
let oldNode = try patchedOldChildren[index].unwrap("updating oldNode")
while nextIndex < newChildren.count {
let newNode = newChildren[nextIndex]
let oldNode = try patchedOldChildren[nextIndex].unwrap("updating oldNode")
try renderNode(new: newNode, old: oldNode)
nextIndex += 1
}
nextIndex = newChildren.count
}

private func skipRenderChildren(newTree: VNode, oldTree: VNode?, isMove: Bool) throws {
Expand Down
32 changes: 1 addition & 31 deletions Sources/React/VDOM/VNode.swift
Original file line number Diff line number Diff line change
@@ -1,36 +1,14 @@
import SRTCore
import SRTDOM

package final class VNode: Hashable {
public struct Equality: Hashable {
enum Kind: Hashable {
case tag(String)
case component(ObjectIdentifier)
}

var kind: Kind
var key: AnyHashable?

init(component: any Component) {
self.kind = if let tag = component as? HTMLElement {
.tag(tag.tagName)
} else {
.component(ObjectIdentifier(type(of: component)))
}

self.key = component.key
}
}

package final class VNode {
public init(
component: any Component
) {
self.component = component
self.equality = Equality(component: component)
}

public let component: any Component
public let equality: Equality

package var instance: Instance? {
get { _instance }
Expand All @@ -45,14 +23,6 @@ package final class VNode: Hashable {
internal weak var _parent: VNode?
private var _children: [VNode] = []

public static func ==(a: VNode, b: VNode) -> Bool {
a.equality == b.equality
}

public func hash(into hasher: inout Hasher) {
hasher.combine(equality)
}

public var parent: VNode? { _parent }

public func removeFromParent() {
Expand Down
74 changes: 74 additions & 0 deletions Sources/React/VDOM/VNodeMatch.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
enum VNodeKind: Hashable {
case html(String)
case text
case component(ObjectIdentifier)
}

extension Component {
var vnodeKind: VNodeKind {
switch self {
case let html as HTMLElement: .html(html.tagName)
case is TextElement: .text
default: .component(ObjectIdentifier(type(of: self)))
}
}

var vnodeDiscriminator: VNodeDiscriminator {
VNodeDiscriminator(kind: vnodeKind, key: key)
}
}

struct VNodeDiscriminator: Hashable {
init(
kind: VNodeKind,
key: AnyHashable?
) {
self.kind = kind
self.key = key
}

var kind: VNodeKind
var key: AnyHashable?
}

struct VNodeMatchAdapter: Hashable {
init(node: VNode) {
self.node = node
self.disc = node.component.vnodeDiscriminator
}

var node: VNode
let disc: VNodeDiscriminator

static func ==(a: Self, b: Self) -> Bool { a.disc == b.disc }

func hash(into hasher: inout Hasher) {
hasher.combine(disc)
}
}

extension VNode {
static func match(newChildren: [VNode], oldChildren: [VNode]) -> CollectionDifference<VNode> {
matchStdlib(newChildren: newChildren, oldChildren: oldChildren)
}

static func matchStdlib(newChildren: [VNode], oldChildren: [VNode]) -> CollectionDifference<VNode> {
let newChildren = newChildren.map { VNodeMatchAdapter(node: $0) }
let oldChildren = oldChildren.map { VNodeMatchAdapter(node: $0) }

let diff: CollectionDifference<VNodeMatchAdapter> = newChildren
.difference(from: oldChildren)
.inferringMoves()

return CollectionDifference<VNode>(
diff.map { (patch) in
switch patch {
case .remove(offset: let o, element: let e, associatedWith: let a):
.remove(offset: o, element: e.node, associatedWith: a)
case .insert(offset: let o, element: let e, associatedWith: let a):
.insert(offset: o, element: e.node, associatedWith: a)
}
}
)!
}
}

0 comments on commit b8667da

Please sign in to comment.