State

So far we've been loosely inspired by React. If you're familiar with React, you'll know that React components have two elements:

  • props: passed from the parent. These roughly correspond to our parameters passed to the +new method.
  • state: internal to the component, this holds implementation details that the parent should not have to know about. The canonical example is whether some text should be rendered fully, or truncated with a "Continue Reading…" link. This is a detail the parent component should not have to manually manage.

Figuring out the difference between these two can be tricky at first. Thinking in React is a really helpful blog post on this topic.

Just like React, CKComponent has state.

@interface CKComponent
- (void)updateState:(id (^)(id))updateBlock mode:(CKUpdateMode)mode;
@end

Let's make a simple example of using state for the "Continue Reading…" link.

#import "CKComponentSubclass.h" // import to expose updateState:
@implementation MessageComponent
+ (id)initialState
{
return @NO;
}
+ (instancetype)newWithMessage:(NSAttributedString *)message
{
CKComponentScope scope(self);
NSNumber *state = scope.state();
return [super newWithComponent:
[CKTextComponent
newWithAttributes:{
.attributedString = message,
.maximumNumberOfLines = [state boolValue] ? 0 : 5,
}
viewAttributes:{}
accessibilityContext:{}]];
}
- (void)didTapContinueReading
{
[self updateState:^(id oldState){ return @YES; } mode:CKUpdateModeAsynchronous];
}
@end

That's all there is to it. Some nice attributes:

  • Continue Reading state is completely hidden from parent components and controllers. They don't need to know about it or manage it.
  • State changes can be coalesced or arbitrarily delayed for performance reasons. We can easily compute the updated component off the main thread when possible/desired.