Implement node ref updates in TestRenderer (#51)
This is a prerequisite for #49, the actual `UIKitRenderer` implementation will follow in a separate PR.
This commit is contained in:
parent
c9ae90e96f
commit
5fabe139dd
|
@ -15,7 +15,7 @@ public struct AnyNode: Equatable {
|
|||
lhs.props == rhs.props
|
||||
}
|
||||
|
||||
let ref: AnyObject?
|
||||
public let ref: AnyObject?
|
||||
public let props: AnyEquatable
|
||||
public let children: AnyEquatable
|
||||
let type: ComponentType
|
||||
|
@ -115,7 +115,7 @@ extension CompositeComponent {
|
|||
|
||||
extension RefComponent {
|
||||
public static func node(
|
||||
ref: Ref<RefType>,
|
||||
ref: Ref<RefTarget?>,
|
||||
_ props: Props,
|
||||
_ children: Children
|
||||
) -> AnyNode {
|
||||
|
|
|
@ -13,8 +13,24 @@ public protocol AnyHostComponent {}
|
|||
|
||||
public protocol HostComponent: AnyHostComponent, Component {}
|
||||
|
||||
public protocol RefComponent: HostComponent {
|
||||
associatedtype RefType
|
||||
public protocol AnyRefComponent {
|
||||
static func update(ref: AnyObject, with value: Any)
|
||||
}
|
||||
|
||||
public protocol RefComponent: AnyRefComponent, HostComponent {
|
||||
associatedtype RefTarget
|
||||
}
|
||||
|
||||
extension AnyRefComponent where Self: RefComponent {
|
||||
public static func update(ref: AnyObject, with value: Any) {
|
||||
guard let ref = ref as? Ref<RefTarget?>,
|
||||
let value = value as? RefTarget else {
|
||||
assertionFailure("failed to cast objects passed to \(#function)")
|
||||
return
|
||||
}
|
||||
|
||||
ref.value = value
|
||||
}
|
||||
}
|
||||
|
||||
/// Type-erased version of `CompositeComponent` to work around
|
||||
|
|
|
@ -28,13 +28,21 @@ public final class TestRenderer: Renderer {
|
|||
let result = TestView(component.node)
|
||||
parent.add(subview: result)
|
||||
|
||||
update(target: result, with: component)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
public func update(
|
||||
target: TestView,
|
||||
with component: TestRenderer.MountedHost
|
||||
) {}
|
||||
) {
|
||||
guard
|
||||
let componentType = component.type as? AnyRefComponent.Type,
|
||||
let anyRef = component.node.ref else { return }
|
||||
|
||||
componentType.update(ref: anyRef, with: target)
|
||||
}
|
||||
|
||||
public func unmount(
|
||||
target: TestView,
|
||||
|
|
|
@ -10,6 +10,10 @@ import XCTest
|
|||
|
||||
@testable import Tokamak
|
||||
|
||||
extension Button: RefComponent {
|
||||
public typealias RefTarget = TestView
|
||||
}
|
||||
|
||||
extension Int: Updatable {
|
||||
public enum Action {
|
||||
case increment
|
||||
|
@ -39,10 +43,14 @@ struct Test: LeafComponent {
|
|||
let state1 = hooks.custom()
|
||||
let state2 = hooks.custom()
|
||||
let state3 = hooks.custom()
|
||||
let ref = hooks.ref(type: TestView.self)
|
||||
|
||||
return StackView.node([
|
||||
Button.node(.init(onPress: Handler { state1.set { $0 += 1 } }),
|
||||
"Increment"),
|
||||
Button.node(
|
||||
ref: ref,
|
||||
.init(onPress: Handler { state1.set { $0 += 1 } }),
|
||||
"Increment"
|
||||
),
|
||||
Label.node("\(state1.value)"),
|
||||
Button.node(.init(onPress: Handler { state2.set { $0 + 1 } }),
|
||||
"Increment"),
|
||||
|
@ -67,12 +75,15 @@ final class HooksTests: XCTestCase {
|
|||
let button2Props = stack.subviews[2].props(Button.Props.self),
|
||||
let button2Handler = button2Props.handlers[.touchUpInside]?.value,
|
||||
let button3Props = stack.subviews[4].props(Button.Props.self),
|
||||
let button3Handler = button3Props.handlers[.touchUpInside]?.value
|
||||
let button3Handler = button3Props.handlers[.touchUpInside]?.value,
|
||||
let button1Ref = stack.subviews[0].node.ref as? Ref<TestView?>
|
||||
else {
|
||||
XCTAssert(false, "components have no handlers")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertTrue(button1Ref.value === stack.subviews[0])
|
||||
|
||||
button1Handler(())
|
||||
|
||||
button2Handler(())
|
||||
|
|
Loading…
Reference in New Issue