Skip to content

Commit 96f31a6

Browse files
kingsleyadiofacebook-github-bot
authored andcommitted
Integrate StateProvider with State
Summary: Incorporates several changes into State when read tracking is enabled - State will now read its value via the StateProvider - State equality and hashCode also now ignore the fallback value A number of changes might need to be made to prepare the codebase for these changes (especially with the identity of state), but these will happen in a separate diff stack [RFC](https://docs.google.com/document/d/1aFOON5LBtuTA9xibwv5WGqPtZi5SU3dquIMso7bw7hY/edit?pli=1&tab=t.0) Reviewed By: astreet Differential Revision: D72053550 fbshipit-source-id: 2b6eb759180a07460b76f31b1825e6c7fc67f7ce
1 parent ce8a771 commit 96f31a6

9 files changed

Lines changed: 70 additions & 19 deletions

File tree

litho-core/src/main/java/com/facebook/litho/ComponentContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public class ComponentContext {
8282
private boolean mIsParentTreePropsCloned;
8383

8484
@ThreadConfined(ThreadConfined.ANY)
85-
private @Nullable LithoTree mLithoTree;
85+
private @Nullable final LithoTree mLithoTree;
8686

8787
private @Nullable LithoVisibilityEventsController mLithoVisibilityEventsController;
8888

litho-core/src/main/java/com/facebook/litho/ComponentTree.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
import com.facebook.litho.debug.LithoDebugEventAttributes;
8686
import com.facebook.litho.lifecycle.LifecycleOwnerWrapper;
8787
import com.facebook.litho.perfboost.LithoPerfBooster;
88+
import com.facebook.litho.state.TreeStateProvider;
8889
import com.facebook.litho.stats.LithoStats;
8990
import com.facebook.rendercore.LogLevel;
9091
import com.facebook.rendercore.MountContentPools;
@@ -119,6 +120,7 @@
119120
public class ComponentTree
120121
implements LithoVisibilityEventsListener,
121122
StateUpdater,
123+
TreeStateProvider,
122124
MountedViewReference,
123125
ErrorComponentReceiver,
124126
LithoTreeLifecycleProvider {

litho-core/src/main/java/com/facebook/litho/KState.kt

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ package com.facebook.litho
1818

1919
import com.facebook.litho.annotations.Hook
2020
import com.facebook.litho.state.ComponentState
21+
import com.facebook.litho.state.StateId
22+
import com.facebook.litho.state.StateProvider
23+
import com.facebook.litho.state.StateReadRecorder
24+
import java.util.Objects
2125

2226
/**
2327
* Declares a state variable within a Component. The initializer will provide the initial value if
@@ -33,8 +37,7 @@ fun <T> ComponentScope.useState(initializer: () -> T): State<T> {
3337
val treeState: TreeState =
3438
resolveContext?.treeState
3539
?: throw IllegalStateException("Cannot create state outside of layout calculation")
36-
val stateUpdater: StateUpdater =
37-
context.stateUpdater ?: throw IllegalStateException("LithoTree is null")
40+
val lithoTree = context.lithoTree ?: error("LithoTree is null")
3841

3942
val isNestedTreeContext = context.isNestedTreeContext
4043
val kState =
@@ -54,11 +57,13 @@ fun <T> ComponentScope.useState(initializer: () -> T): State<T> {
5457
context.scopedComponentInfo.state = state
5558

5659
return State(
57-
stateUpdater,
60+
lithoTree.stateProvider,
61+
lithoTree.stateUpdater,
5862
hookIndex,
5963
globalKey,
6064
isNestedTreeContext,
6165
context.componentScope,
66+
lithoTree.isReadTrackingEnabled,
6267
state.value.states[hookIndex] as T)
6368
} else {
6469
context.scopedComponentInfo.state = kState
@@ -70,25 +75,38 @@ fun <T> ComponentScope.useState(initializer: () -> T): State<T> {
7075
}
7176

7277
return State(
73-
stateUpdater,
78+
lithoTree.stateProvider,
79+
lithoTree.stateUpdater,
7480
hookIndex,
7581
globalKey,
7682
isNestedTreeContext,
7783
context.componentScope,
84+
lithoTree.isReadTrackingEnabled,
7885
kState.value.states[hookIndex] as T)
7986
}
8087

8188
/** Interface with which a component gets the value from a state or updates it. */
8289
class State<T>
8390
internal constructor(
91+
private val stateProvider: StateProvider,
8492
private val stateUpdater: StateUpdater,
8593
private val hookStateIndex: Int,
8694
private val globalKey: String,
87-
private val isNestedTreeContext: Boolean,
95+
internal val isNestedTreeContext: Boolean,
8896
private val componentScope: Component?,
89-
val value: T
97+
private val isReadTrackingEnabled: Boolean,
98+
private val fallback: T
9099
) {
91100

101+
internal val stateId: StateId = StateId(stateProvider.treeId, globalKey, hookStateIndex)
102+
103+
val value: T
104+
get() {
105+
if (!isReadTrackingEnabled) return fallback
106+
StateReadRecorder.read(stateId)
107+
return stateProvider.getValue(this)
108+
}
109+
92110
/**
93111
* Updates this state value and enqueues a new layout calculation reflecting it to execute in the
94112
* background.
@@ -215,10 +233,12 @@ internal constructor(
215233

216234
return globalKey == other.globalKey &&
217235
hookStateIndex == other.hookStateIndex &&
218-
value == other.value
236+
if (isReadTrackingEnabled) stateId.treeId == other.stateId.treeId
237+
else fallback == other.fallback
219238
}
220239

221240
override fun hashCode(): Int {
222-
return globalKey.hashCode() * 17 + (value?.hashCode() ?: 0) * 11 + hookStateIndex
241+
return Objects.hash(
242+
globalKey, hookStateIndex, if (isReadTrackingEnabled) stateId.treeId else fallback)
223243
}
224244
}

litho-core/src/main/java/com/facebook/litho/LithoTree.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,28 @@
1717
package com.facebook.litho
1818

1919
import com.facebook.infer.annotation.ThreadConfined
20+
import com.facebook.litho.config.ComponentsConfiguration
21+
import com.facebook.litho.state.StateProvider
22+
import com.facebook.litho.state.StateProviderImpl
23+
import com.facebook.litho.state.TreeStateProvider
2024
import java.util.concurrent.atomic.AtomicInteger
2125
import java.util.concurrent.atomic.AtomicReference
2226

2327
/** Represents a pointer to the Tree that a ComponentContext is attached to */
2428
class LithoTree
25-
constructor(
29+
internal constructor(
30+
treeStateProvider: TreeStateProvider,
2631
@field:ThreadConfined(ThreadConfined.ANY) val stateUpdater: StateUpdater,
2732
@field:ThreadConfined(ThreadConfined.UI) val mountedViewReference: MountedViewReference,
2833
val errorComponentReceiver: ErrorComponentReceiver,
2934
val lithoTreeLifecycleProvider: LithoTreeLifecycleProvider,
3035
val id: Int
3136
) {
37+
@field:ThreadConfined(ThreadConfined.ANY)
38+
val stateProvider: StateProvider = StateProviderImpl(id, treeStateProvider)
39+
40+
val isReadTrackingEnabled: Boolean =
41+
ComponentsConfiguration.defaultInstance.enableStateReadTracking
3242

3343
// Used to lazily store a CoroutineScope, if coroutine helper methods are used.
3444
@JvmField val internalScopeRef: AtomicReference<Any> = AtomicReference<Any>()
@@ -39,6 +49,7 @@ constructor(
3949

4050
fun create(componentTree: ComponentTree, stateUpdater: StateUpdater): LithoTree =
4151
LithoTree(
52+
componentTree,
4253
stateUpdater,
4354
componentTree,
4455
componentTree,

litho-core/src/main/java/com/facebook/litho/NestedLithoPrimitive.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,16 +180,14 @@ fun createdNestedTreeComponentContext(
180180
errorComponent: (Component?) -> Unit,
181181
lifecycleProvider: NestedLithoTreeLifecycleProvider,
182182
): ComponentContext {
183+
val stateUpdater = NestedStateUpdater(state = updatedState, updater = updater)
183184
return ComponentContext(
184185
androidContext,
185186
treeProps,
186187
lithoConfiguration,
187188
LithoTree(
188-
stateUpdater =
189-
NestedStateUpdater(
190-
state = updatedState,
191-
updater = updater,
192-
),
189+
treeStateProvider = stateUpdater,
190+
stateUpdater = stateUpdater,
193191
mountedViewReference = rootHostReference,
194192
errorComponentReceiver = errorComponent,
195193
lithoTreeLifecycleProvider = lifecycleProvider,

litho-core/src/main/java/com/facebook/litho/NestedLithoTree.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import android.view.View
2020
import androidx.annotation.UiThread
2121
import com.facebook.kotlin.compilerplugins.dataclassgenerate.annotation.DataClassGenerate
2222
import com.facebook.kotlin.compilerplugins.dataclassgenerate.annotation.Mode
23+
import com.facebook.litho.state.TreeStateProvider
2324
import com.facebook.rendercore.SizeConstraints
2425
import com.facebook.rendercore.thread.utils.ThreadUtils.assertMainThread
2526

@@ -154,14 +155,17 @@ object NestedLithoTree {
154155
class NestedStateUpdater(
155156
private val state: TreeState,
156157
private val updater: StateUpdateRequester,
157-
) : StateUpdater {
158+
) : StateUpdater, TreeStateProvider {
158159

159160
override var isFirstMount: Boolean
160161
get() = state.mountInfo.isFirstMount
161162
set(value) {
162163
state.mountInfo.isFirstMount = value
163164
}
164165

166+
override val treeState: TreeState
167+
get() = state
168+
165169
override fun updateHookStateAsync(
166170
globalKey: String,
167171
updateBlock: HookUpdater,

litho-core/src/main/java/com/facebook/litho/PrimitiveComponentScope.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,15 @@ fun PrimitiveComponentScope.useNestedTree(
281281
}
282282

283283
val errorComponentRef = useState { AtomicReference<Component?>(null) }
284-
284+
val stateUpdater = NestedStateUpdater(state = newState, updater = onStateUpdate)
285285
val componentContext =
286286
ComponentContext(
287287
androidContext,
288288
treeProps,
289289
lithoConfig,
290290
LithoTree(
291-
stateUpdater = NestedStateUpdater(state = newState, updater = onStateUpdate),
291+
treeStateProvider = stateUpdater,
292+
stateUpdater = stateUpdater,
292293
mountedViewReference = nestedTreeState.mountedViewReference,
293294
errorComponentReceiver = { errorComponentRef.update(AtomicReference(it)) },
294295
lithoTreeLifecycleProvider = nestedTreeState.treeLifecycleProvider,

litho-core/src/main/java/com/facebook/litho/TreeState.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,16 @@ class TreeState {
268268
return false
269269
}
270270

271+
@Suppress("UNCHECKED_CAST")
272+
internal fun <T> getHookStateValue(
273+
globalKey: String,
274+
hookStateIndex: Int,
275+
isNestedTree: Boolean
276+
): T {
277+
val stateContainer = getState(globalKey, isNestedTree)?.value as KStateContainer
278+
return stateContainer.states[hookStateIndex] as T
279+
}
280+
271281
val pendingStateUpdateTransitions: List<Transition>
272282
get() {
273283
val updateStateTransitions: MutableList<Transition> = ArrayList()

litho-core/src/main/java/com/facebook/litho/state/StateProvider.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ internal class StateProviderImpl(
9191
}
9292

9393
override fun <T> getValue(state: State<T>): T {
94-
error("Not yet implemented")
94+
val stateId = state.stateId
95+
require(stateId.treeId == treeId) {
96+
"State tree (id=${stateId.treeId}) does not match StateProvider tree (id=$treeId)"
97+
}
98+
val source = currentSource.get() ?: treeStateProvider.treeState
99+
return source.getHookStateValue(stateId.globalKey, stateId.index, state.isNestedTreeContext)
95100
}
96101
}

0 commit comments

Comments
 (0)