Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: cascader #3007

Open
wants to merge 4 commits into
base: feat_v3.x
Choose a base branch
from

Conversation

oasis-cloud
Copy link
Collaborator

@oasis-cloud oasis-cloud commented Feb 20, 2025

Summary by CodeRabbit

  • 新功能

    • 添加了全新示例,展示组件在不同平台下的多种使用场景。
  • 增强功能

    • 优化了异步数据加载、状态管理和事件回调处理,提升了选项层级展示和交互体验。
    • 更新了 Cascader 组件的属性,强制要求 valuedefaultValue 为必填项。
  • 文档更新

    • 更新了使用说明,新增了非受控用法和部分数据动态加载章节,并调整了部分翻译内容以增强本地化支持。

Copy link

coderabbitai bot commented Feb 20, 2025

Warning

Rate limit exceeded

@oasis-cloud has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 0 minutes and 49 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between df3c570 and 9636bc6.

📒 Files selected for processing (15)
  • src/packages/cascader/cascader.taro.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo1.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo2.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo3.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo4.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo5.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo6.tsx (3 hunks)
  • src/packages/cascader/demos/h5/demo7.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo1.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo2.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo3.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo4.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo5.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo6.tsx (3 hunks)
  • src/packages/cascader/demos/taro/demo7.tsx (1 hunks)

Walkthrough

此次变更主要集中在 Cascader 组件及其测试、demo、文档、类型定义和工具函数的重构与优化。测试文件中删除了与 Tree 类相关的逻辑,组件文件调整了属性和方法签名,引入了异步加载选项的处理,同时 demo 示例和相关文档也做了对应调整。部分状态管理命名统一为 visible、value 和 options,工具函数重命名为 normalizeOptions/normalizeListOptions,并添加了新的工具方法 isEmpty。

Changes

File(s) Change Summary
src/packages/cascader/__tests__/cascader.spec.tsx 删除了 Tree 类及其相关函数(formatTree、convertListToOptions)对应的测试,调整异步 onLoad 测试描述。
src/packages/cascader/cascader.taro.tsx
src/packages/cascader/cascader.tsx
修改 CascaderProps,继承 CascaderPopupProps;更新 onLoad、onChange、onPathChange 签名,新增 onTabsChange;优化内部状态管理。
src/packages/cascader/demo.taro.tsx
src/packages/cascader/demo.tsx
更新 demo 组件:引入 Demo7;调整翻译键(customStyle 改为 title5,新增 uncontrolled);将部分 Taro 组件转换为标准 HTML 元素。
src/packages/cascader/demos/h5/*
src/packages/cascader/demos/taro/*
各 demo 组件中统一重构状态变量命名(isVisible、value、options);异步加载选项、更新 onChange 及 onPathChange 处理逻辑;新增 demo7 组件。
src/packages/cascader/doc*.md 文档更新:新增“基础用法-非受控”和“部分数据动态加载”章节;更新 onLoad、onChange、onPathChange 的属性说明和签名。
src/packages/cascader/types.ts 移除 CascaderOption 接口中的 level 属性,添加索引签名;引入新类型 CascaderActions。
src/packages/cascader/utils.ts 重构函数:formatTree 改为 normalizeOptions,convertListToOptions 改为 normalizeListOptions;调整 Tree 类以适应新的选项归一化逻辑。
src/utils/is-empty.ts 新增 isEmpty 工具函数,用于判断对象、数组或参数是否为空。

Sequence Diagram(s)

sequenceDiagram
    participant U as 用户
    participant C as Cascader组件
    participant A as 异步onLoad函数
    U->>C: 选择一个选项
    C->>A: 调用onLoad(选项, level)
    A-->>C: 返回Promise及子选项数据
    C->>C: 更新状态与选项数据
    U->>C: 触发onChange确认选择
Loading

Possibly related PRs

Suggested reviewers

  • xiaoyatong
  • irisSong

Poem

我是一只快乐的小兔,
在代码森林中跳跃前行。
优化测试与组件并肩作战,
状态与异步流程都变得清晰。
工具函数悄然添新意,
文档记录每一步改进的奇迹。
代码世界因精致而更加美丽 🐰✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai 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

Documentation and Community

  • 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.

@github-actions github-actions bot added action:review This PR needs more reviews (less than 2 approvals) 3.x Target branch 3.x labels Feb 20, 2025
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: 4

🔭 Outside diff range comments (3)
src/packages/cascader/demos/h5/demo6.tsx (1)

89-94: 🛠️ Refactor suggestion

建议改进事件处理函数的类型安全性

事件处理函数使用了 any 类型,这降低了代码的类型安全性。

-  const onChange = (value: any, path: any) => {
+  const onChange = (value: CascaderOption['value'][], path: CascaderOption[]) => {
     setValue(value)
   }
-  const onPathChange = (value: any, path: any) => {
+  const onPathChange = (value: CascaderOption['value'][], path: CascaderOption[]) => {
     console.log('onPathChange', value, path)
   }
src/packages/cascader/demos/taro/demo4.tsx (1)

1-70: 🛠️ Refactor suggestion

建议提取共享逻辑

此文件与 h5/demo4.tsx 存在大量重复代码。建议提取共享逻辑到一个公共文件中,减少代码重复。

建议:

  1. 创建一个共享的 hooks 文件
  2. 将状态管理和异步加载逻辑移至该文件
  3. 在 H5 和 Taro 版本中复用该逻辑

示例实现:

// hooks/useCascaderDemo.ts
export function useCascaderDemo() {
  const [visible, setVisible] = useState(false)
  const [value, setValue] = useState<string[]>([])
  const [options, setOptions] = useState<CascaderOption[]>([])

  useEffect(() => {
    // 初始化逻辑
  }, [])

  const lazyLoad = async (node: CascaderOption, level: number) => {
    // 异步加载逻辑
  }

  return {
    visible,
    setVisible,
    value,
    setValue,
    options,
    lazyLoad,
  }
}
src/packages/cascader/__tests__/cascader.spec.tsx (1)

142-143: 💡 Verification agent

🧩 Analysis chain

快照测试需要更新

测试用例的快照不匹配,这可能是由于组件的有意更改导致的。请检查更改是否符合预期,然后更新快照。

运行以下命令更新快照:


🏁 Script executed:

#!/bin/bash
# 更新失败的快照测试
npm test -- -u

Length of output: 867


快照更新失败:配置问题需要解决

目前快照测试因预期的组件更改而不匹配,但在执行更新快照命令时(npm test -- -u)出现了配置错误——在加载 vite.config.mts 时缺少依赖 @vitejs/plugin-react。请按以下步骤操作:

  • 确认组件更改:确认快照不匹配是预期的变更结果。
  • 检查并修复依赖配置:请检查项目的 Vite 配置文件以及相关依赖项,确保已正确安装 @vitejs/plugin-react 或调整配置以匹配当前项目需要。
  • 重新更新快照:配置问题解决后,再次运行 npm test -- -u 来更新快照。

涉及文件:

  • src/packages/cascader/__tests__/cascader.spec.tsx(第142-143行处)
  • vite.config.mts(配置文件中涉及依赖的加载)
🧰 Tools
🪛 GitHub Actions: CI

[error] 143-143: Snapshot Cascader > visible true 1 mismatched

🧹 Nitpick comments (33)
src/packages/cascader/demos/h5/demo6.tsx (2)

19-21: 建议改进 value 状态的类型定义

当前 value 的类型定义为 string[],建议使用更具体的类型来增强类型安全性。

-  const [value, setValue] = useState<string[]>(['浙江', '温州', '鹿城区'])
+  const [value, setValue] = useState<CascaderOption['value'][]>(['浙江', '温州', '鹿城区'])

22-88: 建议添加加载状态管理

异步加载数据时没有处理加载状态,可能会影响用户体验。建议添加 loading 状态来优化交互。

+  const [loading, setLoading] = useState(true)
   useEffect(() => {
+    setLoading(true)
     setTimeout(() => {
       setOptions([
         // ... options ...
       ])
+      setLoading(false)
     }, 300)
   }, [])

然后在 Cascader 组件中使用:

   <Cascader
     visible={visible}
     activeColor="#3768FA"
     value={value}
     title="选择地址"
     options={options}
+    loading={loading}
     // ... other props ...
   />
src/packages/cascader/demos/taro/demo2.tsx (1)

75-77: 建议改进类型定义

onChange 处理函数存在以下问题:

  1. 使用了 any 类型,降低了类型安全性
  2. path 参数未被使用,但仍被声明

建议按如下方式优化:

- const onChange = (value: any, path: any) => {
+ const onChange = (value: string[]) => {
    setValue(value)
  }
src/packages/cascader/utils.ts (1)

3-22: 递归地规范化选项结构时,应注意对 children 的类型检查。
当前对 children 的处理默认假设其为数组,若后续外部传参出现非数组类型,可能导致 normalizeOptions 抛出错误。可在递归调用前确保对 children 进行判空或类型判断,以增强健壮性。

- children: normalizeOptions(children, keyMap),
+ children: Array.isArray(children)
+   ? normalizeOptions(children, keyMap)
+   : undefined,
src/packages/cascader/demos/h5/demo1.tsx (3)

5-11: 使用 State 管理 visiblevalueoptions 与回调函数 onChange
命名清晰,逻辑简洁。但若有异步需求,可在 onChange 内部添加防抖或节流逻辑,以应对频繁操作。


12-77: 通过定时器模拟异步设置 options 的做法缺少错误处理。
在真实环境中或需调用接口获取数据,可额外捕获错误或取消请求。若只做演示,可在注释中标明示例用途,方便后期维护。


83-85: Celldescription 字段与新状态 value 对应正确。
value 存在多级选中,可考虑在 UI 上进行拼接或展示完整层级路径,提高易用性。

src/packages/cascader/cascader.tsx (4)

2-18: React Hooks 与组件依赖的引入顺序清晰。
这里引入了部分 NutUI 图标组件与内部自定义 Hook,建议定期审视依赖关系,若有重复引用或未使用的依赖应及时清理。


55-74: CascaderProps 接口新增与合并的字段较多,注意字段含义和可选性的一致性。
onLoadonChangeonPathChange 等返回值、参数类型都需要与真实使用场景相符,否则可能引起调用端类型断言失效或业务逻辑混乱。


76-92: defaultProps 使用类型断言时需注意与接口字段保持一致。
如果后续对 CascaderProps 有更新,应同步修正这里的默认值,否则会增大维护成本。


266-343: 渲染级联条目与 Tabs 切换部分在交互可用性上值得优化。

  1. renderCascaderItem 中针对 disabledactiveloading 设置了较好的状态区分。
  2. 如需在移动端适配,可在 className 或样式上增加更细的触控区域提示。
  3. Popup 关闭前,可添加一些过渡动画或二次确认逻辑(根据业务场景),并与 Tabs 的切换逻辑保持一致。
src/packages/cascader/cascader.taro.tsx (4)

2-14: 简化组件依赖的导入顺序有助于阅读与维护。
组件依赖较为分散,建议在后续合并或归类导入,以提高可维护性。


55-74: CascaderProps 接口拓展,支持更多自定义回调与弹窗配置。

  1. onLoadonChangeonPathChangeonTabsChange 函数签名明确,提高可扩展性。
  2. 建议为 valuedefaultValuevisible 等属性添加更详细的 JSDoc 注释,提高团队协作时的可理解度。

236-264: chooseItem 异步加载分支完善,需检查竞态与错误处理。

  • onLoad 调用返回较慢或发生异常时,当前逻辑仅在 try/catch 之后简单打印错误或置空 loading,可考虑增加更多提示或重试机制。
  • 对比 pane.leafpane.children 的判断逻辑,如后续出现伪空数据或字段缺失,可能出现意外分支。

324-343: 切换是否显示 Popup 的条件式渲染处理直观,注意控制底层动画与交互一致。

  • 建议在 Popup 关闭后回调里能清楚区分点击遮罩与点击关闭图标等情况,以便进行更丰富的业务处理。
src/utils/is-empty.ts (1)

1-6: isEmpty 工具函数实现简洁,请谨慎处理特殊类型边界。
例如:若传入 new Date()NaN、或类似无可枚举属性的对象,需要防范意外判定。可考虑补充更多单元测试覆盖场景。

src/packages/cascader/types.ts (2)

15-16: 使用索引签名 [key: string]: any 扩展 CascaderOption 的灵活性。
在不一致的团队协作环境下,需明确约定可能添加的额外字段含义,避免导致数据结构不规范。


40-43: 新增 CascaderActions 类型,开放 open()close() 方法。
该设计便于父级组件或外部管理弹窗状态,但可考虑是否需要更多状态同步回调来满足复杂业务场景。

src/packages/cascader/demos/h5/demo3.tsx (2)

25-27: 建议改进 onChange 的类型定义

当前 onChange 的参数使用了 any 类型,建议使用更具体的类型定义以提高类型安全性。

-  const onChange = (value: any, path: any) => {
+  const onChange = (value: string[], path: CascaderOption[]) => {
     setValue(value)
   }

8-24: 优化异步加载逻辑

loadCascaderItemData 函数有以下几点建议:

  1. 将 500ms 的延迟时间提取为常量
  2. leaf 判断条件 (level >= 2) 建议添加注释说明原因
+  const LOAD_DELAY = 500
   const loadCascaderItemData = (
     node: CascaderOption,
     level: number
   ): Promise<CascaderOption[]> => {
     return new Promise((resolve) => {
-      setTimeout(() => {
+      setTimeout(() => {
         const { value } = node
         const text = value?.toString().substring(0, 1)
         const value1 = `${text}${level + 1}1`
         const value2 = `${text}${level + 1}2`
         resolve([
+          // 当层级达到2时设置为叶子节点
           { value: value1, text: value1, leaf: level >= 2 },
           { value: value2, text: value2, leaf: level >= 2 },
         ])
-      }, 500)
+      }, LOAD_DELAY)
     })
   }
src/packages/cascader/demos/taro/demo3.tsx (1)

25-27: 建议改进 onChange 的类型定义

与 H5 版本相同,建议改进类型定义以提高代码质量。

-  const onChange = (value: any, path: any) => {
+  const onChange = (value: string[], path: CascaderOption[]) => {
     setValue(value)
   }
src/packages/cascader/demos/h5/demo5.tsx (2)

14-27: 建议优化初始化逻辑

useEffect 中的延迟初始化有以下建议:

  1. 将 300ms 延迟时间提取为常量
  2. 考虑添加加载状态指示器
  3. 建议添加错误处理机制
+  const INIT_DELAY = 300
+  const [loading, setLoading] = useState(false)

   useEffect(() => {
+    setLoading(true)
     setTimeout(() => {
       setValue(['广东省', '广州市'])
       setOptions([
         // ... options
       ])
+      setLoading(false)
-    }, 300)
+    }, INIT_DELAY)
   }, [])

29-31: 改进 onChange 的类型安全性

建议使用更具体的类型定义替代 any。

-  const onChange = (value: any, path: any) => {
+  const onChange = (value: string[], path: CascaderOption[]) => {
     setValue(value)
   }
src/packages/cascader/demos/h5/demo4.tsx (2)

25-28: 建议改进类型注解

node: any 的类型注解过于宽松,建议使用更具体的类型。

-  node: any,
+  node: CascaderOption,

42-44: 建议补充类型注解

change4 函数的参数类型应该更明确。

- const change4 = (value: any, path: any) => {
+ const change4 = (value: string[], path: CascaderOption[]) => {
src/packages/cascader/demos/taro/demo1.tsx (2)

8-10: 建议改进类型安全性

建议为 onChange 函数参数添加更具体的类型定义,避免使用 any

-  const onChange = (value: any, path: any) => {
+  const onChange = (value: string[], path: CascaderOption[]) => {
     setValue(value)
   }

11-77: 建议优化异步加载逻辑

  1. 建议将超时时间抽取为常量或配置项
  2. 建议添加错误处理和加载状态
+  const [loading, setLoading] = useState(false)
+  const LOAD_TIMEOUT = 300
   useEffect(() => {
+    setLoading(true)
     setTimeout(() => {
       setOptions([...])
+      setLoading(false)
-    }, 300)
+    }, LOAD_TIMEOUT)
+    return () => setLoading(false)
   }, [])
src/packages/cascader/demos/h5/demo7.tsx (2)

8-11: 建议移除生产环境的调试代码

建议移除 console.log 语句,或使用环境变量控制调试输出。

   const onChange = (value: any, path: any) => {
-    console.log('onchange', value, path)
     setValue(value)
   }
   // ...
   onPathChange={(value, path) => {
-    console.log(value, path)
   }}

Also applies to: 102-104


15-77: 建议抽取共享的选项数据

建议将重复的选项数据抽取到共享的配置文件中。

+// cascaderOptions.ts
+export const defaultOptions = [
+  {
+    value: '浙江',
+    text: '浙江',
+    children: [...]
+  },
+  // ...
+]

// demo7.tsx
+import { defaultOptions } from './cascaderOptions'
useEffect(() => {
  setTimeout(() => {
-    setOptions([...])
+    setOptions(defaultOptions)
  }, 300)
}, [])
src/packages/cascader/demos/taro/demo6.tsx (2)

22-88: 建议将超时时间提取为常量

建议将 useEffect 中的超时时间 300 提取为一个有意义的常量,以提高代码的可维护性。

+ const LOADING_DELAY = 300
  useEffect(() => {
    setTimeout(() => {
      setOptions([
        // ...options
-     ], 300)
+     ], LOADING_DELAY)
  }, [])

89-91: 建议添加类型注解

onChange 处理函数的参数类型应该更明确。

- const onChange = (value: any, path: any) => {
+ const onChange = (value: string[], path: CascaderOption[]) => {
src/packages/cascader/doc.zh-TW.md (1)

45-46: 建议改进繁体中文表述

建议将"透過"改为"通過",使表述更符合繁体中文的使用习惯。

- Cascader 內部透過 `value` 和 `onLoad` 實作了自動載入資料的邏輯
+ Cascader 內部通過 `value` 和 `onLoad` 實作了自動載入資料的邏輯
🧰 Tools
🪛 LanguageTool

[uncategorized] ~45-~45: 您的意思是“"不"透”?
Context: ...## 動態加載 lazy 屬性表示開啟資料的自動加載,Cascader 內部透過 valueonLoad 實作了自動載入資料的邏輯。 `laz...

(BU)

src/packages/cascader/doc.en-US.md (1)

23-27: 新增 Uncontrolled 示例说明
新增的“Basic Usage - Uncontroled”部分展示了组件在非受控模式下的用法,建议修正标题中的拼写错误(“Uncontroled” → “Uncontrolled”),以保证英文表达的准确性。

提供修改建议:

-### Basic Usage - Uncontroled
+### Basic Usage - Uncontrolled
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5014bf and 201e966.

⛔ Files ignored due to path filters (1)
  • src/packages/cascader/__tests__/__snapshots__/cascader.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (26)
  • src/packages/cascader/__tests__/cascader.spec.tsx (4 hunks)
  • src/packages/cascader/cascader.taro.tsx (1 hunks)
  • src/packages/cascader/cascader.tsx (1 hunks)
  • src/packages/cascader/demo.taro.tsx (1 hunks)
  • src/packages/cascader/demo.tsx (3 hunks)
  • src/packages/cascader/demos/h5/demo1.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo2.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo3.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo4.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo5.tsx (1 hunks)
  • src/packages/cascader/demos/h5/demo6.tsx (3 hunks)
  • src/packages/cascader/demos/h5/demo7.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo1.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo2.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo3.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo4.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo5.tsx (1 hunks)
  • src/packages/cascader/demos/taro/demo6.tsx (3 hunks)
  • src/packages/cascader/demos/taro/demo7.tsx (1 hunks)
  • src/packages/cascader/doc.en-US.md (4 hunks)
  • src/packages/cascader/doc.md (4 hunks)
  • src/packages/cascader/doc.taro.md (4 hunks)
  • src/packages/cascader/doc.zh-TW.md (4 hunks)
  • src/packages/cascader/types.ts (2 hunks)
  • src/packages/cascader/utils.ts (1 hunks)
  • src/utils/is-empty.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: CI
src/packages/cascader/__tests__/cascader.spec.tsx

[error] 143-143: Snapshot Cascader > visible true 1 mismatched

🪛 LanguageTool
src/packages/cascader/doc.zh-TW.md

[uncategorized] ~45-~45: 您的意思是“"不"透”?
Context: ...## 動態加載 lazy 屬性表示開啟資料的自動加載,Cascader 內部透過 valueonLoad 實作了自動載入資料的邏輯。 `laz...

(BU)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (34)
src/packages/cascader/demos/h5/demo6.tsx (3)

1-7: 导入声明看起来不错!

导入了必要的 React hooks 和类型定义,结构清晰。


24-86: 注意选项结构中的潜在用户体验问题

  1. 在不同省份中存在重复的城市名称(如"温州"),这可能会导致用户混淆。
  2. 部分选项被禁用(disabled)但缺少视觉提示,可能影响用户理解。

建议:

  1. 为重复的地名添加更多上下文信息
  2. 为禁用选项添加提示信息

96-122: 渲染结构组织合理!

组件结构清晰,主题配置得当,props 传递正确。

src/packages/cascader/demos/taro/demo2.tsx (2)

1-7: 代码结构优化得当!

状态变量的命名更加规范,类型注解的添加提高了代码的类型安全性。


79-106: 渲染逻辑实现合理!

组件属性与状态管理保持一致,接口实现完整。

src/packages/cascader/utils.ts (2)

1-2: 导入类型定义保持整体可读性良好。
这些类型定义能提高可维护性,但请确保与其他文件的类型引用保持一致,避免重复或冲突。


24-60: 基于父子关系构建层级时,应关注顶层 ID 未匹配的情况。
当前仅返回 map[topId],若 topId 在来源数据中不存在,结果将是 undefined。若业务方需要在此场景下返回空数组,请在返回结果前加一次非空判断或兜底处理。

src/packages/cascader/demos/h5/demo1.tsx (2)

1-2: 引入方式和依赖组件在示例中正常。
导入 useStateuseEffect 并结合 CascaderCell 的使用无明显问题。


100-103: 使用 onPathChange 打印路径时可作为调试信息。
如生产环境注重性能与安全,可移除或限制日志输出,避免跟踪信息过多。

src/packages/cascader/cascader.tsx (1)

32-43: CascaderPopupProps 补充 Popup 属性的做法可提高可扩展性。
请确保与 PopupProps 的字段保持同步更新,避免后续出现功能不一致的风险。

src/packages/cascader/cascader.taro.tsx (5)

16-29: 引入本地工具函数与合并 Props 的逻辑值得肯定。
使用 normalizeListOptionsnormalizeOptionsmergeProps 等方法,能提高代码可读性与可重用性。


32-54: 新增的 CascaderPopupPropsCascaderSupportPopupProps 类型定义层次清晰。
通过 PickOmit 组合出弹窗相关的部分属性,结构更灵活,可读性更强。


94-118: 初始化合并 Props 与基础状态管理逻辑清晰,可读性较好。

  • forwardRef 便于父级直接调用开放的 CascaderActions
  • 使用自定义 Hook (useRefState) 与 getRefValue 管理 options,思路明确。

120-140: 基于 usePropsValue 管理受控与非受控的混合状态,提升灵活度。

  • 请确认注释掉的 onPathChange 调用是否为刻意保留,若不再使用可移除,避免他人误解。
  • isEmpty 判断与 normalizeListOptionsnormalizeOptions 配合,确保容错性。

144-169: 使用 useMemo 动态计算级联层级的逻辑值得肯定,但需留意 end 的计算分支。
若仅依赖 opt?.children 判断是否结束,部分情况下(如空数组、缺失字段)可能导致逻辑分支遗漏。建议完善数据校验,确保下游使用安全。

src/packages/cascader/demos/taro/demo5.tsx (2)

14-27: 建议优化初始化逻辑

与 H5 版本相同,建议改进初始化逻辑:

  1. 提取延迟时间常量
  2. 添加加载状态
  3. 添加错误处理

29-31: 改进 onChange 的类型安全性

与 H5 版本保持一致,建议改进类型定义。

src/packages/cascader/demo.tsx (3)

2-2: 导入路径优化和新增演示组件

导入路径的修改和新增 Demo7 组件的集成都很合理。

Also applies to: 9-9


15-15: 翻译键命名优化

翻译键的重命名和新增都保持了良好的一致性,使代码更易维护。

Also applies to: 20-20, 29-29, 33-33, 38-38


47-48: 新增非受控模式演示

新增的非受控模式演示补充了组件的使用场景。

src/packages/cascader/demo.taro.tsx (1)

47-62:

❓ Verification inconclusive

请验证 HTML 元素在 Taro 环境中的兼容性

将 Taro 组件替换为 HTML 元素(div, h2)可能会影响跨平台兼容性。建议验证在各端的表现。


🏁 Script executed:

#!/bin/bash
# 搜索项目中 Taro 组件的使用模式
rg -A 2 "ScrollView|View" --type tsx

Length of output: 66


检查 Taro 环境下 HTML 元素兼容性

建议在非 WEB 平台(例如小程序)上验证 demo.taro.tsx 中直接使用 HTML 标签(div, h2)的效果,确认其兼容性。当前代码将 Taro 提供的组件替换为普通 HTML 元素,可能导致部分平台显示或功能异常。请参考 Taro 官方文档,确保在所有目标平台上均无问题。如有兼容性问题,建议将相关 HTML 标签替换为 Taro 组件(例如 View、Text 等)。

  • 文件:src/packages/cascader/demo.taro.tsx(第47-62行)
  • 检查点:各目标平台(如微信小程序、支付宝小程序等)的元素渲染与交互表现
src/packages/cascader/demos/h5/demo4.tsx (1)

5-7: 状态管理优化

状态变量的命名更加统一和语义化,使用 useState 和类型注解的方式也更加规范。

src/packages/cascader/demos/taro/demo7.tsx (2)

8-11: 移除生产环境的调试代码

与 H5 版本存在相同的问题,建议移除或控制调试输出。

Also applies to: 102-104


15-77: 抽取共享的选项数据

与 H5 版本存在相同的问题,建议将选项数据抽取到共享配置中。

src/packages/cascader/__tests__/cascader.spec.tsx (1)

215-221: 异步测试实现优化

异步加载测试的实现已优化,使用 Promise 和 setTimeout 提供了更清晰的异步行为。

src/packages/cascader/doc.md (2)

23-31: 文档结构清晰完整

新增的非受控用法部分,有助于用户更好地理解组件的不同使用方式。


54-58: 文档说明准确且实用

关于部分数据动态加载的说明非常清晰,特别是强调了无需设置 lazy 属性这一点,可以避免用户混淆。

src/packages/cascader/doc.taro.md (4)

23-27: 新增非受控示例说明
该部分为文档新增了“基础用法-非受控”示例,展示在非受控模式下如何通过传入 options 列表使用 Cascader。示例结构清晰、易于理解。


45-46: 更新动态加载说明
文档中更新了 lazy 属性的描述,明确指出该属性必须与 onLoad 同时设置,并且声明了 onLoad 方法的返回类型为 CascaderOption[]。这有助于开发者正确使用自动加载逻辑。


56-57: 新增部分数据动态加载说明
此处新增说明针对已设置初始 options 的场景,详细描述了如何通过 onLoad 方法实现部分数据的动态加载,并特别注明无需设置 lazy 属性,内容表达清晰。


105-107: 更新事件回调签名说明
Props 表中对 onLoadonChange 以及 onPathChange 的签名进行了更新,确保文档描述与组件实现保持一致,同时增强了类型安全性。

src/packages/cascader/doc.en-US.md (3)

45-46: 更新动态加载说明
文档中对 lazy 属性与 onLoad 回调的描述进行了更新,明确说明二者需要同时设置,并指出了 onLoad 方法返回数据类型为 CascaderOption[]。该说明有助于用户正确理解自动加载的逻辑。


56-57: 更新部分数据动态加载说明
这一部分详细解释了在设置初始 options 后如何通过 onLoad 方法加载数据,并明确告知用户在此场景下无需设置 lazy 属性,描述清晰明了。


105-107: 更新事件回调签名说明
Props 表中对 onLoadonChangeonPathChange 的签名已更新,确保文档和组件实现之间的一致性,增强了类型安全性,文档说明完整。

Comment on lines +93 to 264
state.lazyLoadMap.delete(node)
}

const close = () => {
setInnerVisible(false)
onClose && onClose()
}

const closePopup = () => {
close()
}

/* type: 是否是静默模式,是的话不触发事件
tabsCursor: tab的索引 */
const chooseItem = async (node: CascaderOption, type: boolean) => {
if ((!type && node.disabled) || !state.panes[state.tabsCursor]) {
return
}
// 如果没有子节点
if (state.tree.isLeaf(node, isLazy())) {
node.leaf = true
state.panes[state.tabsCursor].selectedNode = node
state.panes = state.panes.slice(0, (node.level as number) + 1)
if (!type) {
const pathNodes = state.panes.map((item) => item.selectedNode)
const optionParams = pathNodes.map((item: any) => item.value)
onChange(optionParams, pathNodes)
onPathChange?.(optionParams, pathNodes)
setInnerValue(optionParams)
// 如果需要处理最终结果,可以在这里使用 last
setInnerOptions(parent.children)
} catch (error) {
console.error('Error loading data:', error)
}
setOptionsData(state.panes)
close()
return
}
// 如果有子节点,滑到下一个
if (state.tree.hasChildren(node, isLazy())) {
const level = (node.level as number) + 1

state.panes[state.tabsCursor].selectedNode = node
state.panes = state.panes.slice(0, level)
state.tabsCursor = level
state.panes.push({
nodes: node.children || [],
selectedNode: null,
paneKey: `c${state.tabsCursor + 1}`,
})
setOptionsData(state.panes)
setTabvalue(`c${state.tabsCursor + 1}`)

if (!type) {
const pathNodes = state.panes.map((item) => item.selectedNode)
const optionParams = pathNodes.map((item: any) => item?.value)
onPathChange?.(optionParams, pathNodes)
if (lazy) load()
}, [lazy])

const chooseItem = async (pane: CascaderOption, levelIndex: number) => {
if (pane.disabled) return
const nextValue = innerValue.slice(0, levelIndex)
const nextPathNodes = pathNodes.current.slice(0, levelIndex)
if (pane.value) {
setLoading(!!onLoad && { [levelIndex]: pane.value })
nextValue[levelIndex] = pane.value
nextPathNodes[levelIndex] = pane
pathNodes.current = nextPathNodes
props?.onPathChange?.(nextValue, pathNodes.current)
}
if (onLoad) {
// 叶子节点不操作
if (!pane.leaf) {
const asyncOptions = await onLoad(pane, levelIndex)
// 修改 options 触发渲染逻辑
if (asyncOptions) pane.children = asyncOptions
} else {
setVisible(false)
setValue(nextValue)
}
return
}
state.currentProcessNode = node
if (node.loading) {
return
if (!pane.children && !onLoad) {
setVisible(false)
setValue(nextValue)
}

await invokeLazyLoad(node)
if (state.currentProcessNode === node) {
state.panes[state.tabsCursor].selectedNode = node
chooseItem(node, type)
}
setOptionsData(state.panes)
setInnerValue(nextValue)
setLoading({})
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

核心状态管理逻辑合理,但部分异步控制需进一步规范。

  1. usePropsValueuseRefState 配合可减少不必要的 re-render,但需明确每次数据更新路径,以免覆盖外部传入的 props。
  2. onLoad 处理的异步结果,如出现异常时,目前仅简单地打印日志,建议提供更明确的错误回调或 UI 提示信息。
  3. 当懒加载时,如后续层级数据量较大,onLoad 频繁调用可能带来性能开销,可考虑加缓存策略或分页处理。

Copy link

codecov bot commented Feb 20, 2025

Codecov Report

Attention: Patch coverage is 92.24138% with 18 lines in your changes missing coverage. Please review.

Project coverage is 85.85%. Comparing base (a5014bf) to head (9636bc6).
Report is 8 commits behind head on feat_v3.x.

Files with missing lines Patch % Lines
src/packages/cascader/cascader.tsx 90.10% 18 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           feat_v3.x    #3007      +/-   ##
=============================================
+ Coverage      85.79%   85.85%   +0.06%     
=============================================
  Files            281      282       +1     
  Lines          18527    18281     -246     
  Branches        2810     2766      -44     
=============================================
- Hits           15896    15696     -200     
+ Misses          2626     2580      -46     
  Partials           5        5              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

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

🧹 Nitpick comments (4)
src/packages/address/customRender.tsx (1)

14-15: 建议为 value 属性提供默认值

接口中将 valuedefaultValue 设置为必需属性是一个好的改进,但建议在 defaultProps 中也为 value 提供默认值,以确保组件在未传入该属性时的行为一致性。

 const defaultProps = {
   ...ComponentDefaults,
+  value: [],
   visible: false,
   type: 'custom',
   options: [],
src/packages/address/customRender.taro.tsx (1)

14-15: 建议为 value 属性提供默认值

接口中将 valuedefaultValue 设置为必需属性是一个好的改进,但建议在 defaultProps 中也为 value 提供默认值,以确保组件在未传入该属性时的行为一致性。

 const defaultProps = {
   ...ComponentDefaults,
+  value: [],
   visible: false,
   type: 'custom',
   options: [],
src/packages/address/address.tsx (1)

30-31: 建议为 value 属性提供默认值

接口中将 valuedefaultValue 设置为必需属性是一个好的改进,但建议在 defaultProps 中也为 value 提供默认值,以确保组件在未传入该属性时的行为一致性。

 const defaultProps = {
   ...ComponentDefaults,
+  value: [],
   defaultValue: [],
   type: 'custom',
   options: [],
src/packages/address/address.taro.tsx (1)

31-32: 建议为 value 属性提供默认值

接口中将 valuedefaultValue 设置为必需属性是一个好的改进,但建议在 defaultProps 中也为 value 提供默认值,以确保组件在未传入该属性时的行为一致性。

 const defaultProps = {
   ...ComponentDefaults,
+  value: [],
   defaultValue: [],
   type: 'custom',
   options: [],
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 201e966 and c59b8be.

⛔ Files ignored due to path filters (2)
  • src/packages/address/__test__/__snapshots__/address.spec.tsx.snap is excluded by !**/*.snap
  • src/packages/cascader/__tests__/__snapshots__/cascader.spec.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • src/packages/address/address.taro.tsx (1 hunks)
  • src/packages/address/address.tsx (1 hunks)
  • src/packages/address/customRender.taro.tsx (1 hunks)
  • src/packages/address/customRender.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (2)
src/packages/address/address.tsx (1)

154-170: 验证 CustomRender 组件的 value 属性传递

注意到 CustomRender 组件没有传递 value 属性,这可能会导致类型错误或未定义的行为。建议检查并确保正确传递该属性。

 <CustomRender
   visible={innerVisible}
   closeable
   title={title || locale.address.selectRegion}
   left={renderLeftOnCustomSwitch()}
+  value={value}
   defaultValue={defaultValue}
   closeIcon={closeIcon}
   options={options}
src/packages/address/address.taro.tsx (1)

156-172: 验证 CustomRender 组件的 value 属性传递

注意到 CustomRender 组件没有传递 value 属性,这可能会导致类型错误或未定义的行为。建议检查并确保正确传递该属性。

 <CustomRender
   visible={innerVisible}
   closeable
   title={title || locale.address.selectRegion}
   left={renderLeftOnCustomSwitch()}
+  value={value}
   defaultValue={defaultValue}
   closeIcon={closeIcon}
   options={options}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.x Target branch 3.x action:review This PR needs more reviews (less than 2 approvals) size/XXL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant