ComponentKit/Core/Scope/CKComponentScope.mm (102 lines of code) (raw):

/* * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #import "CKComponentScope.h" #import "CKAnalyticsListener.h" #import "CKComponentScopeHandle.h" #import "CKComponentScopeRoot.h" #import "CKThreadLocalComponentScope.h" #import "CKTreeNode.h" #import <ComponentKit/CKRenderHelpers.h> #import <ComponentKit/CKCoalescedSpecSupport.h> static auto toInitialStateCreator(id (^initialStateCreator)(void), Class componentClass) { return initialStateCreator ?: ^{ return [componentClass initialState]; }; } CKComponentScope::~CKComponentScope() { if (_threadLocalScope != nullptr) { [_node.scopeHandle resolveAndRegisterInScopeRoot:_threadLocalScope->newScopeRoot]; if (_threadLocalScope->systraceListener) { auto const componentTypeName = _node.scopeHandle.componentTypeName ?: "UnkownTypeName"; RCCAssertWithCategory(objc_getClass(componentTypeName) != nil, [NSString stringWithUTF8String:componentTypeName], @"Creating an action from a scope should always yield a class"); [_threadLocalScope->systraceListener didBuildComponent:componentTypeName]; } _threadLocalScope->pop(YES, YES); } } CKComponentScope::CKComponentScope(Class __unsafe_unretained componentClass, id identifier, id (^initialStateCreator)(void)) noexcept { RCCAssert(class_isMetaClass(object_getClass(componentClass)), @"Expected %@ to be a meta class", componentClass); RCCWarnWithCategory( [componentClass conformsToProtocol:@protocol(CKReusableComponentProtocol)] == NO, NSStringFromClass(componentClass), @"Reusable components shouldn't use scopes."); RCCAssertWithCategory( identifier == nil || class_isMetaClass(object_getClass(identifier)) || [identifier conformsToProtocol:@protocol(CKComponentProtocol)] == NO, NSStringFromClass(componentClass), @"Identifier should never be an instance of CKComponent. Identifiers should be **constant**."); RCCAssertWithCategory( identifier != componentClass, NSStringFromClass(componentClass), @"Passing the component class as the identifier is redundant."); _threadLocalScope = CKThreadLocalComponentScope::currentScope(); if (_threadLocalScope != nullptr) { RCCWarnWithCategory( [componentClass isSubclassOfClass:[CKComponent class]] == _threadLocalScope->enforceCKComponentSubclasses, NSStringFromClass(componentClass), @"Component type doesn't match the TLS's type. Have you created the component **outside** a component provider function?"); const auto componentTypeName = class_getName(componentClass); [_threadLocalScope->systraceListener willBuildComponent:componentTypeName]; const auto& pair = _threadLocalScope->stack.top(); const auto childPair = [CKTreeNode childPairForPair:pair newRoot:_threadLocalScope->newScopeRoot componentTypeName:componentTypeName identifier:identifier keys:_threadLocalScope->keys.top() initialStateCreator:toInitialStateCreator(initialStateCreator, componentClass) stateUpdates:_threadLocalScope->stateUpdates requiresScopeHandle:YES]; _node = childPair.node; const auto ancestorHasStateUpdate = _threadLocalScope->coalescingMode == RCComponentCoalescingModeComposite && _threadLocalScope->buildTrigger == CKBuildTriggerStateUpdate && (_threadLocalScope->ancestorHasStateUpdate.top() || CKRender::componentHasStateUpdate( _node, pair.previousNode, _threadLocalScope->buildTrigger, _threadLocalScope->stateUpdates)); _threadLocalScope->push({.node = childPair.node, .previousNode = childPair.previousNode}, YES, ancestorHasStateUpdate); } RCCAssertWithCategory(_threadLocalScope != nullptr, NSStringFromClass(componentClass), @"Component with scope must be created inside component provider function."); } id CKComponentScope::state(void) const noexcept { return _node.state; } CKTreeNodeIdentifier CKComponentScope::identifier(void) const noexcept { return _node.nodeIdentifier; } void CKComponentScope::replaceState(const CKComponentScope &scope, id state) noexcept { [scope._node.scopeHandle replaceState:state]; } CKComponentStateUpdater CKComponentScope::stateUpdater(void) const noexcept { // We must capture `node` in a local, since `this` may be destroyed by the time the block executes. CKTreeNode *const node = _node; return ^(id (^stateUpdate)(id), NSDictionary<NSString *, id> *userInfo, CKUpdateMode mode) { [node.scopeHandle updateState:stateUpdate metadata:{.userInfo = userInfo} mode:mode]; }; } CKTreeNode *CKComponentScope::node(void) const noexcept { return _node; }