Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A couple compiler errors on XCode 6.3 beta #1

Closed
mrjjwright opened this issue Mar 25, 2015 · 1 comment · Fixed by #7
Closed

A couple compiler errors on XCode 6.3 beta #1

mrjjwright opened this issue Mar 25, 2015 · 1 comment · Fixed by #7

Comments

@mrjjwright
Copy link

Thanks so much for ComponentKit! It's awesome, the example scrolls so smooth! Of course I switched over to 6.2 but just letting you all know (you probably already do) about these:

/Users/jwright/Downloads/ios_examples/componentkit/ComponentTextKit/TextKit/CKTextKitRendererCache.mm:28:12: Out-of-line definition of 'Key' does not match any declaration in 'CK::TextKit::Renderer::Key'

and

/Users/jwright/Downloads/ios_examples/componentkit/Examples/WildeGuess/Pods/Headers/Build/ComponentKit/CKTextKitRendererCache.h:69:9: Constructor cannot have a return type

@adamjernst
Copy link
Contributor

Thanks! We're on it.

ocrickard pushed a commit that referenced this issue Mar 21, 2017
facebook-github-bot pushed a commit that referenced this issue Jun 16, 2020
Summary:
## Current state

###  CKRenderComponent

It’s pretty simple since there’s no render-to-nil support for `CKRenderComponent`.

1. A controller gets allocated before its component.
2. A controller only gets invalidated / deallocated when the component is *removed* from the new scope root.
3. Thus, a component that renders-to-nil will never have its controller invalidated / deallocated until the component is removed for good (or ad-aeternam).

### CKCompositeComponent

Before we start there’s a subtlety I’d like to mention which is the location of the `return nil`. As a component returns `nil`, it might do so before (Case 1) or after (Case 2) creating its `CKComponentScope`. The difference being that in one case its decision can only depend on its props while in the other it *might* (but not necessarily) also depend on its state.

While this is true for the component itself that returns `nil`, any ancestor that end up returning `nil` as an effect of their descendant returning `nil` are most likely to do so past their own scope initialisation. See this illustrative example:

```
implementation ParentComponent

+ (instancetype)new
{
  CKComponentScope scope(self);

  // Case 2: Component returns nil post-scope
  return [[super newWithComponent:[ChildComponent newWithProps:{}]];
}

end

implementation ChildComponent

+ (instancetype)newWithProps:(const Props&)props
{
  if (props.ABC) {
    // Case 1: Component returns nil pre-scope.
    return nil;
  }

  CKComponentScope scope(self);

  if (scope.state().XYZ) {
    // Case 2: Component returns nil post-scope.
    return nil;
  }

  return [super newWithComponent:[CKComponent new]];
}

end
```

If a component’s render-to-nil decision happens past the component scope, its state (along with all its ancestors states affected by the decision) needs to be carried over to the new tree even though there won’y be any component (as the decision *might* be derived from its state value).

While most components probably do not depend on their state for the render-to-nil decision, my instinct tells me that the majority will end up return nil *after* their or (perhaps more largely) their ancestor’s scope initialisation.

When it comes to the controller, the following rules apply:

1. A controller gets allocated *after* the first generation of component that doesn’t render-to-nil.
2. If a component renders-to-nil before its scope, its controller will be invalidated and deallocated (if a previous generation wasn’t nil) or simply not created if there was no non-nil previous generation.
3. If a component returns-to-nil *after* its scope, its controller will be kept around without being invalidated until the component is *removed* from the hierarchy.
4. It a component renders, then renders to nil, then re-renders the controller from the first non-nil generation will be used (thus `-initWithComponent:` & `-didInit` will not be re-called with the first non-nil props past the render-to-nil).

While it might be reasonably rare, I reckon it’s pretty unexpected to have a controller carry over from previously rendered-to-nil components. Moreover, this causes a few issues we probably shouldn’t have to deal with:

* `self.component` can still be nil if a component renders to nil after not rendering to nil.
* I think it’d be pretty unexpected to still have the controller alive assuming you’ve rendered once (and can lead to crashes).

## This diff

I'm making the logic more expected for composite components. What I propose is:

* A controller gets allocated *after* the first generation of component that doesn’t render-to-nil. (no change).
* When a component renders-to-nil, its controller will be invalidated and deallocated (no registration).
* If the same component subsequently re-renders, then #1 applies.

Nothing will really change for render components.

Reviewed By: kfirapps

Differential Revision: D21999015

fbshipit-source-id: e008965da96cb8805f164115c69bd3e89c7e0941
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants