Skip to content

Conversation

XinRoom
Copy link

@XinRoom XinRoom commented Sep 3, 2025

修复 FormApi.setValues 函数 values 多层嵌套 object 时无法合并导致依然是默认值的问题

Description

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)

Checklist

ℹ️ Check all checkboxes - this will indicate that you have done everything in accordance with the rules in CONTRIBUTING.

  • Run the tests with pnpm test.
  • Changes in changelog are generated from PR name. Please, make sure that it explains your changes in an understandable manner. Please, prefix changeset messages with feat:, fix:, perf:, docs:, or chore:.
  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Summary by CodeRabbit

  • Bug Fixes

    • More predictable form value updates: deep-merge now only happens when the incoming value is an object, preventing unintended merges so primitives and non-object values fully replace existing entries; array and date handling unchanged.
  • Refactor

    • Streamlined internal merge logic for form updates without changing public APIs.

修复 FormApi.setValues 函数 values 多层嵌套 object 时无法合并导致依然是默认值的问题
Copy link

changeset-bot bot commented Sep 3, 2025

⚠️ No Changeset found

Latest commit: d77396a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Sep 3, 2025

Walkthrough

Tightens FormApi.setValues merge behavior: only performs in-place recursive merge when the incoming value is an object; otherwise assigns the incoming value directly. Preserves Dayjs/Date and array checks; adds comments and returns false to signal the key was handled.

Changes

Cohort / File(s) Summary of changes
FormApi merge behavior
packages/@core/ui-kit/form-ui/src/form-api.ts
Adjusted setValues merge: add check that incoming value is an object before recursing; when both sides are objects, call fieldMergeFn(obj[key], value) (in-place) and return false; otherwise assign obj[key] = value and return false. Kept Dayjs/Date and array handling; added explanatory comments.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant FormApi as FormApi.setValues
  participant Merge as fieldMergeFn

  Caller->>FormApi: setValues(partialValues)
  loop for each key,value in partialValues
    alt obj[key] is non-array object AND value is object
      rect rgb(220,240,255)
      Note over FormApi,Merge: Changed: recurse only when both are objects
      FormApi->>Merge: fieldMergeFn(obj[key], value)
      Merge-->>FormApi: merged in-place
      end
      FormApi-->>FormApi: return false (handled)
    else if value is Array
      Note over FormApi: Array handling (unchanged)
      FormApi-->>FormApi: default assign/merge
    else if value is Dayjs/Date
      Note over FormApi: Dayjs/Date checks (unchanged)
    else
      Note over FormApi: Direct assignment for non-object value
      FormApi-->>FormApi: obj[key] = value
      FormApi-->>FormApi: return false (handled)
    end
  end
  FormApi-->>Caller: done
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • mynetfan
  • anncwb
  • jinmao88

Poem

A nibble of code, a hop through keys,
I merge when both are leafy trees.
If one is not, I place it plain—
No fuzzy copies, just tidy gain.
Thump, thump, done — the form feels sane. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6a85b3a and 00bb25f.

📒 Files selected for processing (1)
  • packages/@core/ui-kit/form-ui/src/form-api.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/@core/ui-kit/form-ui/src/form-api.ts (2)
packages/@core/base/shared/src/utils/inference.ts (1)
  • isObject (160-160)
packages/@core/base/shared/src/utils/date.ts (2)
  • isDayjsObject (24-26)
  • isDate (20-22)
⏰ 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). (2)
  • GitHub Check: post-update (ubuntu-latest)
  • GitHub Check: post-update (windows-latest)
🔇 Additional comments (2)
packages/@core/ui-kit/form-ui/src/form-api.ts (2)

339-349: Solid fix: only deep-merge object→object and skip default merge.

This closes the multi-layer object merge gap while preserving Dayjs/Date and array behavior on the target side. The in-place merge is appropriate for avoiding unintended overwrites.


339-349: Double-check vee-validate meta/dirty updates with in-place nested writes.

Because nested merges bypass setValues for those top-level keys, ensure vee-validate still flags fields as dirty/touched as expected. If not, consider emitting the top-level key in filteredFields (with a shallow reference bump) to drive form meta updates.

Comment on lines 339 to 349
if (!Array.isArray(obj[key]) &&
isObject(obj[key]) &&
!isDayjsObject(obj[key]) &&
!isDate(obj[key]) &&
isObject(value)) { // 添加对 value 也是对象的检查
// 递归合并对象
fieldMergeFn(obj[key], value);
} else {
obj[key] = value; // 直接赋值
}
return false; // 返回 false 表示已经处理,不需要默认合并
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Also guard the incoming value for arrays/Dayjs/Date to avoid accidental deep-merge or no-op.

If isObject(value) returns true for arrays or Date-like objects in your util, we might deep-merge where we should assign, or silently do nothing (e.g., merging a Date). Recommend symmetric checks on value.

Apply this minimal diff:

-        if (!Array.isArray(obj[key]) &&
-            isObject(obj[key]) &&
-            !isDayjsObject(obj[key]) &&
-            !isDate(obj[key]) &&
-            isObject(value)) { // 添加对 value 也是对象的检查
+        if (!Array.isArray(obj[key]) &&
+            isObject(obj[key]) &&
+            !isDayjsObject(obj[key]) &&
+            !isDate(obj[key]) &&
+            isObject(value) &&
+            !Array.isArray(value) &&
+            !isDayjsObject(value) &&
+            !isDate(value)) { // 双方均为纯对象才递归合并
           // 递归合并对象
           fieldMergeFn(obj[key], value);
         } else {
           obj[key] = value; // 直接赋值
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!Array.isArray(obj[key]) &&
isObject(obj[key]) &&
!isDayjsObject(obj[key]) &&
!isDate(obj[key]) &&
isObject(value)) { // 添加对 value 也是对象的检查
// 递归合并对象
fieldMergeFn(obj[key], value);
} else {
obj[key] = value; // 直接赋值
}
return false; // 返回 false 表示已经处理,不需要默认合并
if (!Array.isArray(obj[key]) &&
isObject(obj[key]) &&
!isDayjsObject(obj[key]) &&
!isDate(obj[key]) &&
isObject(value) &&
!Array.isArray(value) &&
!isDayjsObject(value) &&
!isDate(value)) { // 双方均为纯对象才递归合并
// 递归合并对象
fieldMergeFn(obj[key], value);
} else {
obj[key] = value; // 直接赋值
}
return false; // 返回 false 表示已经处理,不需要默认合并
🤖 Prompt for AI Agents
In packages/@core/ui-kit/form-ui/src/form-api.ts around lines 339 to 349, the
merge branch only checks obj[key] for arrays/Dayjs/Date but not the incoming
value, which can cause accidental deep-merge of arrays/Dayjs/Date or no-op on
Date-like objects; update the conditional to also guard the incoming value by
requiring !Array.isArray(value) && !isDayjsObject(value) && !isDate(value) (in
addition to isObject(value)) so only plain objects are recursively merged and
all others are assigned.

💡 Verification agent

🧩 Analysis chain

Confirm createMerge and isObject contracts to ensure no surprises.

  • Verify isObject excludes arrays and Date/Dayjs; otherwise the above guard is required.
  • Confirm createMerge’s callback semantics (returning false filters the handled key from the resulting patch, and obj is the target). This underpins the “in-place merge + filteredFields” flow.

Run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== isObject implementation ==="
sed -n '1,220p' packages/@core/base/shared/src/utils/inference.ts | rg -nC3 'export (const|function) isObject'

echo -e "\n=== createMerge definition & docs ==="
fd -t f --full-path 'packages' -g '**/*.ts' | rg -nP 'export (const|function)\s+createMerge|function createMerge' -n -H -C3

echo -e "\n=== createMerge usage examples (to confirm return false semantics) ==="
rg -nP --glob '!**/node_modules/**' 'createMerge\(' -C2

Length of output: 203


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "===== find isObject across repo ====="
rg -nP "\bisObject\b" -g "!**/node_modules/**" -C2 || echo "No matches for isObject"

echo

echo "===== find createMerge across repo ====="
rg -nP "\bcreateMerge\b" -g "!**/node_modules/**" -C2 || echo "No matches for createMerge"

Length of output: 9491


Strengthen object‐guards & fix merge‐callback return

  • isObject returns true for arrays and Date objects; extend the value check to also exclude arrays, Date and Dayjs before recursing.
  • createMerge (alias of createDefu) requires the merger callback to return true when it has applied a custom merge (to skip the default behavior); change the existing return false to return true. (npmjs.com)
🤖 Prompt for AI Agents
In packages/@core/ui-kit/form-ui/src/form-api.ts around lines 339 to 349,
strengthen the object guard and fix the merge callback return: when checking
value before recursing, ensure you exclude arrays, Date objects and Dayjs
objects (similar to the checks for obj[key]) so you only recurse for plain
objects; and change the merger callback to return true (not false) to signal to
createMerge/createDefu that the custom merge was applied and default merging
should be skipped.

@XinRoom
Copy link
Author

XinRoom commented Sep 4, 2025

出现了一个新bug,数组类型的合并后不是取设定值,而是设定值和默认值进行了合并

/**
     * antd的日期时间相关组件的值类型为dayjs对象
     * element-plus的日期时间相关组件的值类型可能为Date对象
     * 以上两种类型需要排除深度合并
     */
    const fieldMergeFn = createMerge((obj, key, value) => {
      if (key in obj) {
        if (!Array.isArray(obj[key]) &&
            isObject(obj[key]) &&
            !isDayjsObject(obj[key]) &&
            !isDate(obj[key])){ // 添加对 value 也是对象的检查
          // 递归合并对象
          fieldMergeFn(obj[key], value);
        } else {
          console.log('fieldMergeFn', key, obj[key], value);
          obj[key] = value; // 直接赋值
        }
        return false; // 返回 false 表示已经处理,不需要默认合并
      }
      return true;
    });
    const filteredFields = fieldMergeFn(fields, form.values);
    console.log('filteredFields', filteredFields);
    this.handleStringToArrayFields(filteredFields);
    form.setValues(filteredFields, shouldValidate);


fieldMergeFn username undefined admin
form-api.ts:346 fieldMergeFn nickname undefined admin
form-api.ts:346 fieldMergeFn phone undefined 
form-api.ts:346 fieldMergeFn email undefined 
form-api.ts:346 fieldMergeFn remark undefined 
form-api.ts:346 fieldMergeFn role_ids undefined Proxy(Array) {0: 1}
form-api.ts:355 filteredFields {
    "status": "activated",
    "username": "admin",
    "nickname": "admin",
    "phone": "",
    "email": "",
    "role_ids": [
        1,
        1
    ],
    "remark": ""
}

我将非isObject的return true;后,修复了这个问题,但是很奇怪,我不能理解

修复 vbenjs@00bb25f 下 Array 类型设定值与默认值合并后出现和叠加问题

setValues({a:[1]},{a:[1]})  => {a:[1,1]}
Copy link
Contributor

@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/@core/ui-kit/form-ui/src/form-api.ts (1)

337-355: Swap return value in object branch to skip default merge
In packages/@core/ui-kit/form-ui/src/form-api.ts (lines 337–343), change the object-merge branch to return true instead of return false. createMerge is an alias for defu’s createDefu, whose callback must return true when it has applied custom merging to prevent the default deep-merge behavior (npmjs.com).

♻️ Duplicate comments (1)
packages/@core/ui-kit/form-ui/src/form-api.ts (1)

339-350: Fix merge-callback return and guard incoming value to stop array duplication.

Returning false after the recursive merge lets the default merge continue, which concatenates arrays (e.g., role_ids[1,1]). Also guard value against Array/Dayjs/Date so only plain objects recurse.

-        if (!Array.isArray(obj[key]) &&
-            isObject(obj[key]) &&
-            !isDayjsObject(obj[key]) &&
-            !isDate(obj[key]) &&
-            isObject(value)) { // 添加对 value 也是对象的检查
+        if (!Array.isArray(obj[key]) &&
+            isObject(obj[key]) &&
+            !isDayjsObject(obj[key]) &&
+            !isDate(obj[key]) &&
+            isObject(value) &&
+            !Array.isArray(value) &&
+            !isDayjsObject(value) &&
+            !isDate(value)) { // 双方均为纯对象才递归合并
           // 递归合并对象
           fieldMergeFn(obj[key], value);
-          return false;
+          return true; // 已处理,跳过默认合并,避免数组被拼接
         } else {
           obj[key] = value; // 直接赋值
           return true;
         }
🧹 Nitpick comments (1)
packages/@core/ui-kit/form-ui/src/form-api.ts (1)

321-357: Add unit tests for merge behavior (arrays replace; Date/Dayjs assign; deep objects merge).

Cover cases:

  • Arrays replace, not concat: default [1] + incoming [1][1].
  • Deep object merge with plain objects only.
  • Date/Dayjs assigned (no deep merge).
  • Mixed: object with nested array replaced.

I can draft vitest specs targeting FormApi.setValues with these scenarios.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 00bb25f and d77396a.

📒 Files selected for processing (1)
  • packages/@core/ui-kit/form-ui/src/form-api.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/@core/ui-kit/form-ui/src/form-api.ts (2)
packages/@core/base/shared/src/utils/inference.ts (1)
  • isObject (160-160)
packages/@core/base/shared/src/utils/date.ts (2)
  • isDayjsObject (24-26)
  • isDate (20-22)
⏰ 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). (2)
  • GitHub Check: post-update (ubuntu-latest)
  • GitHub Check: post-update (windows-latest)

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.

1 participant