Skip to content

Commit 4bbb042

Browse files
Fix uncontrolled multiline TextInput not resizing when children change
## Summary When a multiline TextInput uses children (attributed text) instead of the `value` prop (uncontrolled mode), changing the children does not cause the TextInput to resize. For example, clearing children after sending a message leaves the input at its expanded multi-line height. ## Root Cause `measureContent()` is called by Yoga during `YGNodeCalculateLayout`, which runs BEFORE `updateStateIfNeeded()` in the `layout()` callback. In `attributedStringBoxToMeasure()`, when the state is meaningful and the `attributedStringBox` is non-empty, it returns the stale native text for measurement — even though the React tree (children) has already changed. The sequence: 1. User types multiline text → native `_updateState()` updates `attributedStringBox` with the typed text 2. User clears text (e.g. sends message) → React children become empty 3. Yoga calls `measureContent()` → `attributedStringBoxToMeasure()` returns the stale multi-line text → height stays expanded 4. `updateStateIfNeeded()` runs later and updates state, but the layout has already been computed with the wrong size ## Fix In `attributedStringBoxToMeasure()`, check if the React tree attributed string has diverged from what's stored in state. If so, use the React tree version for measurement instead of the stale `attributedStringBox`. This ensures Yoga measures with the correct content when children change between layout passes. ## Changelog [iOS][Fixed] - Fix uncontrolled multiline TextInput not resizing when children change Fixes #54570
1 parent 2d78a39 commit 4bbb042

File tree

1 file changed

+18
-0
lines changed

1 file changed

+18
-0
lines changed

packages/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputShadowNode.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,24 @@ class BaseTextInputShadowNode
204204
layoutContext.fontSizeMultiplier;
205205
if (meaningfulState) {
206206
const auto &stateData = BaseShadowNode::getStateData();
207+
208+
// Check if the react tree has changed since the last state update.
209+
// This happens with uncontrolled TextInputs where native text differs
210+
// from what React children produce (e.g. after clearing text on send).
211+
// In that case, measureContent() is called by Yoga before
212+
// updateStateIfNeeded() runs (which happens in layout(), after
213+
// YGNodeCalculateLayout), so stateData.attributedStringBox still
214+
// contains the stale native text. Use the react tree for measurement
215+
// instead to get the correct size.
216+
const auto &reactTreeAttributedString = getAttributedString(layoutContext);
217+
if (!stateData.reactTreeAttributedString.isContentEqual(reactTreeAttributedString)) {
218+
auto attributedString = reactTreeAttributedString;
219+
if (attributedString.isEmpty()) {
220+
attributedString = getPlaceholderAttributedString(layoutContext);
221+
}
222+
return AttributedStringBox{attributedString};
223+
}
224+
207225
auto attributedStringBox = stateData.attributedStringBox;
208226
if (attributedStringBox.getMode() == AttributedStringBox::Mode::OpaquePointer ||
209227
!attributedStringBox.getValue().isEmpty()) {

0 commit comments

Comments
 (0)