Skip to content

Commit

Permalink
Add diagrams and lifecycle to docs
Browse files Browse the repository at this point in the history
  • Loading branch information
arkivanov committed Jun 9, 2021
1 parent 20f49b6 commit 9c93536
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 49 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ Decompose is a Kotlin Multiplatform lifecycle-aware business logic components (a
* [StateKeeper](https://arkivanov.github.io/Decompose/component/state-keeper/) - preserve state during configuration changes and/or process death
* [InstanceKeeper](https://arkivanov.github.io/Decompose/component/instance-keeper/) - retain instances in your components (similar to AndroidX ViewModel)

### Component hierarchy

<img src="docs/media/ComponentHierarchy.png" width="512">

### Pluggable UI hierarchy

<img src="docs/media/PluggableUiHierarchy.png" width="512">

### Typical component structure

<img src="docs/media/ComponentStructure.png" width="512">

## Samples

Check out the [project website](https://arkivanov.github.io/Decompose/samples/) for a full description of each sample.
Expand Down
47 changes: 47 additions & 0 deletions docs/component/lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Lifecycle

The component lifecycle is very similar to the [Android Activity lifecycle](https://developer.android.com/guide/components/activities/activity-lifecycle). There is the [Lifecycle](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/lifecycle/Lifecycle.kt) interface, each instance of the `ComponentContext` has its own instance of the `Lifecycle`.

There is also [LifecycleRegistry](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/lifecycle/LifecycleRegistry.kt) which is the `Lifecycle` and the `Lifecycle.Callbacks` at the same time. `LifecycleRegistry` can be used to manually control the lifecycle. It can be passed to a root component, or can be used in tests.

Each component has its own lifecycle. The lifecycle of a child component can not be longer than its parent's lifecycle.

<img src="https://raw.githubusercontent.com/arkivanov/Decompose/master/docs/media/LifecycleStates.png" width="512">

The lifecycle can be observed using its `subscribe`/`unsubscribe` methods:

```kotlin
lifecycle.subscribe(
object : DefaultLifecycleCallbacks {
override fun onCreate() {
// Handle lifecycle created
}

// onStart, onResume, onPause, onStop are also available

override fun onDestroy() {
// Handle lifecycle destroyed
}
}
)
```

Or using the extension functions:

```kotlin
lifecycle.subscribe(
onCreate = { /* Handle lifecycle created */ },
// onStart, onResume, onPause, onStop are also available
onDestroy = { /* Handle lifecycle destroyed */ }
)

lifecycle.doOnCreate {
// Handle lifecycle created
}

// doOnStart, doOnResume, doOnPause, doOnStop are also available

lifecycle.doOnDestroy {
// Handle lifecycle destroyed
}
```
111 changes: 62 additions & 49 deletions docs/component/overview.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,68 @@

# Component Overview

Every component represents a piece of logic with lifecycle and optional pluggable UI.
A component is just a normal class that encapsulates some logic and possibly another (child) components. Every component has its own lifecycle, which is automatically managed by Decompose. So everything encapsulated by a component is scoped. Please head to the [Lifecycle documentation page](https://arkivanov.github.io/Decompose/router/lifecycle/) for more information.

UI is optional and is pluggable from outside of components. Components do not depend on UI, the UI depends on components.

## Component hierarchy

<img src="https://raw.githubusercontent.com/arkivanov/Decompose/master/docs/media/ComponentHierarchy.png" width="512">

## Pluggable UI hierarchy

<img src="https://raw.githubusercontent.com/arkivanov/Decompose/master/docs/media/PluggableUiHierarchy.png" width="512">

## Typical component structure

<img src="https://raw.githubusercontent.com/arkivanov/Decompose/master/docs/media/ComponentStructure.png" width="512">

## `ComponentContext`

Each component has an associated [`ComponentContext`](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContext.kt) which implements the following interfaces:

* [RouterFactory](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/RouterFactory.kt), so you can create nested `Routers` in your `Componenets`
* [StateKeeperOwner](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/statekeeper/StateKeeperOwner.kt), so you can preserve any state during configuration changes and/or process death
* [InstanceKeeperOwner](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/instancekeeper/InstanceKeeperOwner.kt), so you can retain instances in your components (like with AndroidX ViewModels)
* [LifecycleOwner](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/lifecycle/LifecycleOwner.kt), so each component has its own lifecycle
* [BackPressedDispatcherOwner](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/backpressed/BackPressedDispatcherOwner.kt), so each component can handle back button events

So if a component requires any of the above features, just pass the `ComponentContext` via the component's constructor. You can use the delegation pattern to add the `ComponentContext` to `this` scope:

```kotlin
class Counter(
componentContext: ComponentContext
) : ComponentContext by componentContext {

// The rest of the code
}
```

When instantiating a root component we have to create `ComponentContext` manually. There is [DefaultComponentContext](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/DefaultComponentContext.kt) which is the default implementation class of the `ComponentContext`. There are also handy helper functions provided by Jetpack/JetBrains Compose extension modules.

## Child components

Decompose provides ability to organize components into trees, so each parent component is only aware of its immediate children. Hence the name of the library - "Decompose". You decompose your project by multiple independent reusable components. When adding a sub-tree into another place (reusing), you only need to satisfy its top component's dependencies.

There are two common ways to add a child component:

- Using the `Router` - prefer this option when a navigation between components is required. Please head to the [Router documentation page](https://arkivanov.github.io/Decompose/router/overview/) for more information.
- Adding a component as a permanent child - prefer this option if the child component should exist as long as the parent component.

### Adding a permanent child component

To create the `ComponentContext` for a permanent child component use `ComponentContext.childContext(...)` extension function:

```kotlin
class SomeParent(
componentContext: ComponentContext
) : ComponentContext by componentContext {

private val counter = Counter(childContext(key = "Counter"))
}
```

> ⚠️ Never pass parent's `ComponentContext` to children, always use either the `Router` or the `childContext(...)` function.
## Examples

Expand Down Expand Up @@ -62,51 +123,3 @@ struct CounterView: View {
}
}
```

## `ComponentContext`

Each component has an associated [`ComponentContext`](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContext.kt) which implements the following interfaces:

* [RouterFactory](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/RouterFactory.kt), so you can create nested `Routers` in your `Componenets`
* [StateKeeperOwner](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/statekeeper/StateKeeperOwner.kt), so you can preserve any state during configuration changes and/or process death
* [InstanceKeeperOwner](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/instancekeeper/InstanceKeeperOwner.kt), so you can retain instances in your components (like with AndroidX ViewModels)
* [LifecycleOwner](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/lifecycle/LifecycleOwner.kt), so each component has its own lifecycle
* [BackPressedDispatcherOwner](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/backpressed/BackPressedDispatcherOwner.kt), so each component can handle back button events

So if a component requires any of the above features, just pass the `ComponentContext` via the component's constructor. You can use the delegation pattern to add the `ComponentContext` to `this` scope:

```kotlin
class Counter(
componentContext: ComponentContext
) : ComponentContext by componentContext {

// The rest of the code
}
```

When instantiating a root component we have to create `ComponentContext` manually. There is [DefaultComponentContext](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/DefaultComponentContext.kt) which is the default implementation class of the `ComponentContext`. There are also handy helper functions provided by Jetpack/JetBrains Compose extension modules.

## Child components

Decompose provides ability to organize components into trees, so each parent component is only aware of its immediate children. Hence the name of the library - "Decompose". You decompose your project by multiple independent reusable components. When adding a sub-tree into another place (reusing), you only need to satisfy its top component's dependencies.

There are two common ways to add a child component:

- Using the `Router` - prefer this option when a navigation between components is required. Please head to the [Router documentation page](https://arkivanov.github.io/Decompose/router/overview/) for more information.
- Adding a component as a permanent child - prefer this option if the child component should exist as long as the parent component.

### Adding a permanent child component

To create the `ComponentContext` for a permanent child component use `ComponentContext.childContext(...)` extension function:

```kotlin
class SomeParent(
componentContext: ComponentContext
) : ComponentContext by componentContext {

private val counter = Counter(childContext(key = "Counter"))
}
```

> ⚠️ Never pass parent's `ComponentContext` to children, always use either the `Router` or the `childContext(...)` function.
Binary file added docs/media/ComponentHierarchy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/ComponentStructure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/LifecycleStates.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/PluggableUiHierarchy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ nav:

- Component:
- Overview: component/overview.md
- Lifecycle: component/lifecycle.md
- StateKeeper: component/state-keeper.md
- InstanceKeeper: component/instance-keeper.md
- Custom ComponentContext: component/custom-component-context.md
Expand Down

0 comments on commit 9c93536

Please sign in to comment.