Skip to content

Commit 55002b1

Browse files
authored
fix(android): initial measuring tweaks (#784)
# Summary This fixes a bug where the component's initial height calculation would break if `defaultValue` was provided without an explicit `fontSize` in `htmlStyle` prop. The root cause was a mix of a bad initial estimate and invalid measurements caching: During `initialMeasure`, missing a passed `fontSize` in props, meant it defaulted to 0, which caused the entire height estimate to be wrong. I fixed this by adding a fallback to the default font size, matching how `EnrichedText` measurements already handle it. After `initialMeasure`, `setValue` (caused by the present `defaultValue` prop) is run, which tries to update the shadow node state, but `stateWrapper` is still `null`. This caused the `MeasurementStore` internal cache to be out of sync with the actual shadow node's state. Fixed this by providing an early return in `invalidateLayout()` - we don't call `Measurement.store()` at all, if `stateWrapper` is unavailable. I've also tweaked two small things: in `EnrichedTextInputShadowNode.cpp` the `forceHeightRecalculationCounter_` was never initialized before accessing its value, which would make it have random, garbage values. I've also adjusted the provided `forceHeightRecalculationCounter` to the `stateWrapper`, as its value was always pre-increment during `invalidateLayout()`. I believe it's better to have the shadow node's and component's counters' values consistent. Sometimes the `setValue`'s layout invalidation could run before the `initialMeasure`. That made the `MeasurementStore` cached measurements correct from the start, as after `setValue` runs, the actual component's size is measured, not estimated like it's the case with `initialMeasure`. You can see this race condition in the attached video. ## Test Plan I've provided a modified example app, so you can see the bug yourself - you can run ```sh git checkout 212b2a3 ``` and then you can check out how it works after the fix ```sh git checkout 4c47809 ``` ## Screenshots / Videos The race condition in action (stumbled across in a different app): https://github.com/user-attachments/assets/1ded9bcc-ce87-49a1-82d8-3300dd41a4a3 ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ❌ | | Android | ✅ | | Web | ❌ | ## Checklist - [x] E2E tests are passing - [ ] Required E2E tests have been added (if applicable)
1 parent 49da66a commit 55002b1

3 files changed

Lines changed: 6 additions & 5 deletions

File tree

android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewLayoutManager.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@ class EnrichedTextInputViewLayoutManager(
88
private var forceHeightRecalculationCounter: Int = 0
99

1010
fun invalidateLayout() {
11+
val stateWrapper = view.stateWrapper ?: return
12+
1113
val text = view.text
1214
val paint = view.paint
1315

1416
val needUpdate = MeasurementStore.store(view.id, text, paint)
1517
if (!needUpdate) return
1618

17-
val counter = forceHeightRecalculationCounter
1819
forceHeightRecalculationCounter++
1920
val state = Arguments.createMap()
20-
state.putInt("forceHeightRecalculationCounter", counter)
21-
view.stateWrapper?.updateState(state)
21+
state.putInt("forceHeightRecalculationCounter", forceHeightRecalculationCounter)
22+
stateWrapper.updateState(state)
2223
}
2324

2425
fun releaseMeasurementStore() {

android/src/main/java/com/swmansion/enriched/textinput/MeasurementStore.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ object MeasurementStore {
130130
props: ReadableMap?,
131131
): Float {
132132
val propsFontSize = props?.getDouble("fontSize")?.toFloat()
133-
if (propsFontSize == null) return defaultView.textSize
133+
if (propsFontSize == null || propsFontSize <= 0) return defaultView.textSize
134134

135135
return ceil(pixelFromSpOrDp(propsFontSize, allowFontScalingFromProps(props)))
136136
}

android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class EnrichedTextInputShadowNode final
4747
const LayoutConstraints &layoutConstraints) const override;
4848

4949
private:
50-
int forceHeightRecalculationCounter_;
50+
int forceHeightRecalculationCounter_{0};
5151
std::shared_ptr<EnrichedTextInputMeasurementManager> measurementsManager_;
5252
};
5353
} // namespace facebook::react

0 commit comments

Comments
 (0)