티스토리 뷰

Android/Litho

4. GroupSection Spec LifeCycle

Kaboomba 2019. 11. 9. 20:43

GroupSection Spec's Lifecylce looks like following:

GroupSection Spect has lifecycle in order below:

 

@OnCreateInitialState
To set an initial value for a state, you have to write a method annotated with @OnCreateInitialState in your spec.
The first parameter must be of type ComponentContext. StateValue and @Prop are allowed. For example,

@LayoutSpec
object CheckboxSpec {
    @OnCreateInitialState
    fun createInitialState(
        c: ComponentContext,
        isChecked: StateValue<Boolean>,
        @Prop initChecked: Boolean) {
      isChecked.set(initChecked);
    }
}

@OnCreateService
You can create service class in @OnCreateService function if you want to have data persistent or REST API instance with LithoView.@OnCreateService annotated function is called only once when the Section is first created. In the function, you should create and return your service.

@GroupSectionSpec
object ServiceSectionSpec {
  ...
  @OnCreateService
  fun onCreateServices(
    c: SectionContext,
    @Prop config: Configuration,
    ...): DataLoader {
      return DataLoaderFactory.createLoader(config)
  }
}

@OnCreateTreeProp
Each TreeProp is declared and created from a method annotated with @OnCreateTreeProp.

@LayoutSpec
object ParentComponentSpec {
  @OnCreateTreeProp
  fun onCreatePrefetcher(
      c: ComponentContext,
      @Prop prefetcher: Prefetcher): Prefetcher {
    return prefetcher
  }

  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop imageUri: Uri): Component {
    return ChildComponent.create(c)
        .imageUri(imageUri)
        .build()
  }
}

You can only declare one TreeProp for any one given type. If a child of ParentComponent also defines a TreeProp of type Prefetcher, it will override the value of that TreeProp for all its children (but not for itself).

 

@OnCreateChildren
@OnCreateChildren annotated function returns a tree of Sections that will have root in this GroupSectionSpec. The children can be Section instances created from other GroupSectionSpec classes or from DiffSectionSpec classes.

Let’s have a look at how you would declare a simple list that contains a header followed by a list of Strings. We’ll use a SingleComponentSection for the header and a DataDiffSection for the list of Strings and we’ll combine these in a hierarchy.

@GroupSectionSpec
object FooSectionSpec {
  @OnCreateChildren
  fun onCreateChildren(
      c: SectionContext,
      @Prop headerTitle: String,
      @Prop data: List<String>): Children {
    return Children.create()
        .child(
            SingleComponentSection.create(c)
                .component(
                    Text.create(c)
                        .text(headerTitle)
                        .build()))
        .child(
            DataDiffSection.create<String>(c)
                .data(data)
                .renderEventHandler(FooSection.onRender(c)))
        .build()
  }

  @OnEvent(RenderEvent.class)
  fun onRender(
      c: SectionContext,
      @FromEvent model: String): RenderInfo {
    return ComponentRenderInfo.create()
        .component(
            Text.create(c)
                .text(model)
                .build())
        .build()
    }
}

@Event(RenderEvent::class)
Sections work best when combined with the rendering optimisations that Litho Components offer. However, the API also provides support for rendering with Views instead of Components. This makes the transition to Sections easier and you can still take advantage of the performance benefits regardless of your product’s UI using traditional Views, Litho Components or a mix of the two.

View support is only offered by DataDiffSection at the moment. Let’s have another look at the DataDiffSection example to recap how you declare what the framework should render for a certain item.

@GroupSectionSpec
object MyGroupSectionSpec {
  @OnCreateChildren
  fun onCreateChildren(
      c: SectionContext,
      @Prop dataModel: List<MyModel>): Children {
      return Children.create()
          .child(DataDiffSection.create(c)
              .data(dataModel)
           .renderEventHandler(MyGroupSection.onRenderEvent(c)))
          .build()
  }

  @OnEvent(RenderEvent::class)
  fun onRenderEvent(
      c: SectionContext,
      @FromEvent model: MyModel): RenderInfo {
      return ComponentRenderInfo.create()
         .component(MyModelItemComponent.create(c).item(model).build())
          .build()
  }
}

@OnUnbindService
@OnUnbindService provides the callback for you to clean up and undo anything you have done in @OnBindService. @OnUnBindService annotated function is called (along with onBindService()) every time the section tree is updated (usually because of a state update).
This function is passed the service created by onCreateService as the second function parameter. This should be the inverse of onBindService. Anything set or bound in onBindService should be undone here.

@GroupSectionSpec
object ServiceSectionSpec {
  ...
  @OnBindService
  fun onBindService(c: SectionContext, service: DataLoader, ...) {
      service.registerEventLoader(ServiceSection.dataLoaded(c))
  }

  @OnUnbindService
  fun onUnbindService(c: SectionContext, service: DataLoader, ...) {
      service.unregisterEventLoader()
  }

  @OnEvent(YourData.class)
  fun dataLoaded(c: SectionContext, @FromEvent data: Data) {
      // Update your state with the new data
      ServiceSection.updateData(c, data)
  }

  @UpdateState
  fun updateData(connectionData: StateValue<Data>, @Param data: Data) {
      connectionData.set(data);
  }
}

@OnBindService
@OnBindService is a callback that allows you to define how your service should interact with its section. This bridge can be used to pass the new set of data to the section whenever a fetch is completed.
onBindService() is called (along with onUnbindService()) every time
* the section tree is updated (usually because of a state update).
* This function is passed the service created by onCreateService as the second function parameter.
* In this function you should bind any EventHandler that will make state changes to your service.

 

@OnDataBound
A method annotated with this annotation will be called when the data changes corresponding to this section’s hierarchy is made available to the SectionTree.Target.

Data changes could occur due to any number of the following: 1) Insertion 2) Update 3) Delete 4) Move

Availability of these data changes do not mean they are visible on the viewport. To detect visibility, use @OnViewportChanged.

class SectionSpec {

  @OnDataBound
  fun OnDataBound(
    c: SectionContext,
    @Prop prop: YourProp, 
    @State state: YourState) {
    // Handle data changes
  }
}

@OnDataRendered
???

 

@OnViewportChanged
A method annotated with this annotation will be called when there is a change in the visible viewport.
Call SectionTree#viewPortChanged() or SectionTree#viewPortChangedFromScrolling() to allow your sections to know that something on the viewport is now different.

class SectionSpec {

  @OnViewportChanged
  fun onViewportChanged(
    c: SectionContext,
    firstVisiblePosition: Int,
    lastVisiblePosition: Int,
    totalCount: Int,
    firstFullyVisibleIndex: Int,
    lastFullyVisibleIndex: Int,
    @Prop prop: YourProp,
    @State state: YourState) {
  }
}

@OnRefresh
A method annotated with this annotation will be called when the the UI rendered by the section’s hierarchy is requesting for a refresh of the content. You need to handle this annotated function when RecyclerCollectionComponent.disablePTR is true, which means SwipeRefreshLayout works and handle here when refresh starts.
Call SectionTree#refresh() to propagate your refresh request to all the sections in the hierarchy. Then you can handle it in your section like this:

class SectionSpec {

  @OnRefresh
  fun onRefresh(c: SectionContext, @Prop yourProp, @State yourState) {
    // Handle your refresh request
  }
}

@OnUpdateState
You can define how a component’s state or states should be updated by declaring methods annotated with @OnUpdateState in the specs.

You can have as many @OnUpdateState methods as you need, according to what states you want to update or what parameters your states depend on.

Each call to an @OnUpdateState method will trigger a new layout calculation for its ComponentTree. For better performance, if there are situations that can trigger an update for multiple states, you should define an @OnUpdateState method that updates the value for all those states. Bundling them in the same update call reduces the number of new layout calculations and improves performance.

This is what you need to know when writing an @OnUpdateState method:

  • Parameters representing the states must match the name of a parameter annotated with @State and their type must be a StateValue parameterized with the type of the matching @State.
  • @Param parameters are allowed. If the value of your state depends on props, you can declare them like this and pass the value of the prop when the update call is triggered.
  • All other parameters must have a corresponding parameter annotated with @State in the other lifecycle methods, and their type must be a StateValue parameterized with the type of the matching @State element.

Here’s how you would define a state update method for the checkbox:

@LayoutSpec
object CheckboxSpec {
  @OnUpdateState
  fun updateCheckboxState(isChecked: StateValue<Boolean> ) {
    isChecked.set(!isChecked.get())
  }
}

'Android > Litho' 카테고리의 다른 글

6. StateValue  (0) 2019.11.10
5. HeroItemSpec  (0) 2019.11.10
3. Sample Application - HeroesComponentSpec  (0) 2019.11.09
2. Sample Application - Intialisation  (0) 2019.11.09
1. Litho - Get Started  (0) 2019.11.09
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함