Skip to content

Conversation

@Single-Dancer
Copy link
Collaborator

@Single-Dancer Single-Dancer commented Jan 9, 2026

这个 PR 做了什么? (简要描述所做更改)
修复 Input 组件受控模式下无法输入中文

这个 PR 是什么类型? (至少选择一个)

  • 错误修复 (Bugfix) issue: fix #
  • 新功能 (Feature)
  • 代码重构 (Refactor)
  • TypeScript 类型定义修改 (Types)
  • 文档修改 (Docs)
  • 代码风格更新 (Code style update)
  • 构建优化 (Chore)
  • 其他,请描述 (Other, please describe):

这个 PR 涉及以下平台:

  • 所有平台
  • Web 端(H5)
  • 移动端(React-Native)
  • 鸿蒙(Harmony)
  • 鸿蒙容器(Harmony Hybrid)
  • ASCF 元服务
  • 快应用(QuickApp)
  • 所有小程序
  • 微信小程序
  • 企业微信小程序
  • 京东小程序
  • 百度小程序
  • 支付宝小程序
  • 支付宝 IOT 小程序
  • 钉钉小程序
  • QQ 小程序
  • 飞书小程序
  • 快手小程序
  • 头条小程序

Summary by CodeRabbit

发布说明

  • Bug Fixes

    • 改进了输入组件对 IME(输入法)生命周期的处理,修复了中文/日文等组合输入在提交值时的异常。
    • 修复了粘贴、聚焦和按键场景下重复触发输入事件的问题。
  • Improvements

    • 优化了文本长度限制(含数值类型)的处理与最终值确定逻辑。
    • 在组合输入期间改进了显示值同步,提升输入事件触发的准确性与一致性。

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 9, 2026

Walkthrough

修改 Input 组件以引入局部状态 compositionValue,重构输入/合成事件处理流程(包括 compositionstart/update/end),新增 triggerValueChange 用于统一处理值截断与 onInput 触发,渲染使用 compositionValue 优先显示 IME 临时值。

Changes

Cohort / File(s) 变更摘要
Input 组件核心逻辑
packages/taro-components-react/src/components/input/index.tsx
引入 IStatecompositionValue?: string)并将类签名从 React.Component<IProps, null> 改为 React.Component<IProps, IState>;新增 triggerValueChange(value: string, e: any),集中处理 value 强制转换与 maxlength(number)逻辑;重构 handleInputhandlePastehandleFocushandleKeyDownhandleComposition,支持 compositionstart/update/end,管理 isOnCompositiononInputExecutedcompositionValue;渲染使用 displayValue(优先 compositionValue)绑定到 input。

Sequence Diagram(s)

sequenceDiagram
    participant User as 用户
    participant IME as 输入法编辑器
    participant Input as Input 组件
    participant DOM as DOM

    User->>IME: 开始输入(非直接字符)
    IME->>Input: compositionstart
    Input->>Input: isOnComposition = true

    User->>IME: 继续输入/修改
    IME->>Input: compositionupdate
    Input->>Input: 更新 compositionValue
    Input->>DOM: 渲染 compositionValue(displayValue)

    User->>IME: 确认/结束合成
    IME->>Input: compositionend
    Input->>Input: isOnComposition = false
    Input->>Input: 调用 triggerValueChange(finalValue, e)
    Input->>DOM: 更新真实 value 并触发 onInput 事件
Loading

代码审阅工作量

🎯 4 (复杂) | ⏱️ ~45 分钟

建议审阅者

  • tutuxxx
  • zhengyanan1

贺诗

🐰✨ 合成声里细微香,
值在兔耳轻轻藏,
更新触发成一线,
显示优先显临场,
代码跳舞庆改良 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题清晰地总结了主要变更:修复Input组件在受控模式下无法输入中文的问题,与代码变更高度相关。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/input

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Single-Dancer Single-Dancer requested a review from tutuxxx January 9, 2026 02:57
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @packages/taro-components-react/src/components/input/index.tsx:
- Around line 144-173: The compositionend branch in handleComposition sets
this.onInputExecuted = true but relies on a subsequent handleInput to clear it,
which can fail in some WebViews; update handleComposition (compositionend
branch) to schedule a microtask fallback (e.g., Promise.resolve().then(...))
that clears this.onInputExecuted if it remains true after the event loop tick so
the flag cannot get stuck, keeping the existing immediate-true behavior for
normal browsers while ensuring eventual reset in environments that don't fire
the input event.
🧹 Nitpick comments (2)
packages/taro-components-react/src/components/input/index.tsx (2)

100-142: triggerValueChange 修改 DOM 值时建议补一个更稳的边界与类型约束
目前仅对 number 做 maxlength 截断并回写 e.target.value,整体 OK;但建议对 maxlength <= 0value 非字符串(上游传入)做兜底,避免出现意外空串/异常。

建议性改动(更稳的边界/类型兜底)
   triggerValueChange (value: string, e: any) {
@@
-    let finalValue = value
+    let finalValue = value ?? ''
+    if (typeof finalValue !== 'string') finalValue = String(finalValue)
     const inputType = getTrueType(type, confirmType, password)
@@
-    if (inputType === 'number' && finalValue && maxlength <= finalValue.length) {
+    if (inputType === 'number' && maxlength > 0 && finalValue && maxlength <= finalValue.length) {
       finalValue = finalValue.substring(0, maxlength)
       // 如果被截断了,需要同步回 DOM
       if (e.target && e.target.value !== finalValue) {
         e.target.value = finalValue
       }
     }

238-265: compositionupdate 里复用 handleInput 有副作用耦合,建议直接 setState(可选)
现在 compositionupdatehandleInput 会额外执行 stopPropagation、以及受 onInputExecuted 早退逻辑影响;更直接的做法是在 compositionupdate 里只做 setState({ compositionValue: e.target.value })

可选简化
     } else if (e.type === 'compositionupdate') {
       // 拼音输入过程中,保持标记并更新显示
       this.isOnComposition = true
-      // 必须在这里触发 setState 才能让输入框里的拼音实时更新
-      this.handleInput(e)
+      // 直接更新显示值,避免复用 handleInput 带来的副作用耦合
+      this.setState({ compositionValue: e.target.value })
     } else if (e.type === 'compositionend') {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fffb1ce and a592745.

📒 Files selected for processing (1)
  • packages/taro-components-react/src/components/input/index.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build Rust Binding / stable - x86_64-pc-windows-msvc
🔇 Additional comments (2)
packages/taro-components-react/src/components/input/index.tsx (2)

40-69: 组合输入引入本地 compositionValue 状态的方向正确
这套“组合输入期间用内部 state 渲染、结束后再对外触发”的策略能对症解决受控模式下中文输入显示/上屏问题。


293-342: displayValue(compositionValue 优先)+ 绑定 onCompositionUpdate 的渲染路径合理
受控 value 在组合输入期间不再覆盖输入框显示,且补齐 onCompositionUpdate,整体符合预期;另外 omit 掉 onInput 再由内部统一转发也能避免重复触发。

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/taro-components-react/src/components/input/index.tsx (1)

100-142: 注释与实现不一致,容易造成维护误解。

第124行注释说明"只有当值确实改变,或者需要强制触发时才调用",但第125-141行的实现中,只要 typeof onInput === 'function' 为真就直接调用 onInput(e),没有任何值变更检查。该注释没有准确反映代码的实际行为,容易误导后续维护。

🧹 Nitpick comments (2)
packages/taro-components-react/src/components/input/index.tsx (2)

45-50: 注释顺序与代码实现不一致。

第49行注释描述的操作顺序为 compositionend -> triggerValueChange(外部回调) -> onInputExecuted = true,但实际代码中(line 260-263)是先设置 this.onInputExecuted = true 再调用 this.triggerValueChange()。建议修正注释以避免混淆:

♻️ 建议的注释修正
-空格选词 (中文输入法): compositionend -> triggerValueChange(外部回调) -> onInputExecuted = true -> 紧随其后的 handleInput 被拦截退出。
+空格选词 (中文输入法): compositionend -> onInputExecuted = true -> triggerValueChange(外部回调) -> 紧随其后的 handleInput 被拦截退出。

144-173: handleInput 核心逻辑正确,但标志位使用可以优化。

整体逻辑清晰地处理了 composition 期间和普通输入两种场景。不过在第163-171行有个小问题:

this.onInputExecuted = true
// ...
this.triggerValueChange(newValue, e)
this.onInputExecuted = false

这里将 onInputExecuted 设为 true 后立即在同步调用 triggerValueChange 后重置为 false。由于 triggerValueChange 只是调用 props.onInput 回调,不太可能同步触发新的 input 事件,这个立即重置看起来是多余的。

相比之下,compositionend 中设置该标志后不立即重置(line 260),而是依赖后续的 handleInput 调用来重置(line 149)。这种不一致可能引起维护困扰。

建议考虑:

  1. 如果确认 triggerValueChange 不会同步触发事件,可以移除 Case 2 中的标志位设置
  2. 或者在注释中说明为什么需要这样处理
♻️ 简化建议(如确认不需要防护)
    } else {
      // Case 2: 普通输入 (英文、数字、或中文选词后)
-      // 标记执行,防止重复
-      this.onInputExecuted = true

      // 清理中间状态
      if (this.state.compositionValue !== undefined) {
        this.setState({ compositionValue: undefined })
      }

      this.triggerValueChange(newValue, e)
-      this.onInputExecuted = false
    }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a592745 and faf8d0c.

📒 Files selected for processing (1)
  • packages/taro-components-react/src/components/input/index.tsx
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-05-06T06:55:44.077Z
Learnt from: Single-Dancer
Repo: NervJS/taro PR: 17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.077Z
Learning: 在 Taro 的 water-flow 组件中,getRectSizeSync 函数如果找不到指定的节点并且重试次数用尽,会一直处于 pending 状态而不是抛出错误,因为它在 retryTimes <= 0 时直接返回而不 resolve 或 reject Promise。应该使用 Promise.race 添加超时机制来解决这个问题。

Applied to files:

  • packages/taro-components-react/src/components/input/index.tsx
📚 Learning: 2025-05-06T06:55:44.077Z
Learnt from: Single-Dancer
Repo: NervJS/taro PR: 17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.077Z
Learning: 在 Taro 的 water-flow 组件中,getRectSizeSync 函数如果找不到指定的节点,会一直处于 pending 状态而不是抛出错误。在这种情况下,应该使用 Promise.race 添加超时机制,而不是仅依赖 try/catch 来处理错误。

Applied to files:

  • packages/taro-components-react/src/components/input/index.tsx
📚 Learning: 2025-05-06T06:55:44.077Z
Learnt from: Single-Dancer
Repo: NervJS/taro PR: 17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.077Z
Learning: 在 Taro 的 getRectSizeSync 函数中,如果找不到指定的节点,函数会一直处于 pending 状态而不是抛出错误。在这种情况下,使用 try/catch 处理不够充分,需要添加超时处理如 Promise.race 来避免无限等待。

Applied to files:

  • packages/taro-components-react/src/components/input/index.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Build Rust Binding / stable - x86_64-pc-windows-msvc
  • GitHub Check: Build Rust Binding / stable - x86_64-unknown-linux-gnu
  • GitHub Check: Build Rust Binding / stable - x86_64-apple-darwin
  • GitHub Check: Build Rust Binding / stable - x86_64-unknown-linux-musl
  • GitHub Check: Build Rust WASM / stable - wasm32-wasi
  • GitHub Check: Build Rust Binding / stable - x86_64-pc-windows-msvc
  • GitHub Check: Build Rust Binding / stable - x86_64-unknown-linux-musl
  • GitHub Check: Build Rust Binding / stable - x86_64-unknown-linux-gnu
  • GitHub Check: Build Rust WASM / stable - wasm32-wasi
🔇 Additional comments (5)
packages/taro-components-react/src/components/input/index.tsx (5)

40-42: 接口定义清晰。

IState 接口设计合理,compositionValue 作为可选字段能够准确表示 IME 输入的临时状态。


53-69: 构造函数初始化正确。

状态初始化和标志位设置合理,注释清晰说明了 onInputExecuted 的用途。


175-236: 事件处理器中的标志位重置合理。

handlePastehandleFocushandleKeyDown 中重置 onInputExecuted 标志,能够防止标志位异常保持导致后续输入被阻塞,这是防御性编程的良好实践。


280-344: 渲染逻辑正确实现了 IME 输入显示。

关键改动:

  1. 从 state 中获取 compositionValue(line 293)
  2. 计算 displayValue:优先显示 compositionValue,否则显示受控的 value(lines 313-315)
  3. 将 input 的 value 绑定到 displayValue(line 332)
  4. 添加 onCompositionUpdate 事件处理(line 339)

这个实现是整个方案的核心,正确地在 composition 期间显示 IME 中间输入(如拼音),在 composition 结束后显示最终值,完美解决了受控组件下中文输入的问题。


238-265: Composition 事件处理逻辑完全正确。

该方法精妙地处理了 IME 输入的完整生命周期,通过 isOnCompositiononInputExecuted 两个标记,完美适配 Chrome 和其他浏览器的事件顺序差异:

  • compositionstart → compositionupdate → compositionend 的标准流程得到正确实现,compositionupdate 时通过 handleInput 只更新内部拼音显示状态,不向外触发 onChange
  • 防重复触发机制在 compositionend 设置 onInputExecuted = true,后续 handleInput 中消费此标记,确保浏览器的 onChange 事件不会导致二次回调
  • 最终值通知通过 compositionend 中的 triggerValueChange 确保父组件收到最终汉字,而非拼音临时状态

@Single-Dancer Single-Dancer removed the request for review from zhengyanan1 January 9, 2026 03:09
@Single-Dancer Single-Dancer added this to the 4.1.10 milestone Jan 9, 2026
@codecov
Copy link

codecov bot commented Jan 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.32%. Comparing base (fffb1ce) to head (faf8d0c).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main   #18731   +/-   ##
=======================================
  Coverage   56.32%   56.32%           
=======================================
  Files         447      447           
  Lines       23345    23345           
  Branches     5795     5795           
=======================================
  Hits        13149    13149           
- Misses       8366     8376   +10     
+ Partials     1830     1820   -10     
Flag Coverage Δ
taro-cli 72.85% <ø> (ø)
taro-runtime 60.27% <ø> (ø)
taro-web 53.12% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.
see 7 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants