用于设计、审查、生成或重构使用Effector、effector-react、Farfetched、AtomicRouter、@effector/next等库的React应用。
Effector 生态系统技能是一项面向实际任务的技能,主要用于使用此技能来应用使用或计划使用的前端应用程序 : React 或 React- concompatible UI. 效果器用于商业逻辑和状态;Effector- react for binding un。
实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;
涉及批量任务时,还应保存进度,避免中断后重复操作。该技能适合用于一次性任务,也可以接入自动化工作流,与其他技能或上层代理配合完成更完整的业务链路;在组合使用时,应明确每一步的输入输出关系,并避免不同步骤之间出现参数冲突。
在以下前端应用中使用该技能(当前已采用或计划采用):
effector-react 将 Effector 单元绑定至组件@withease/contracts 或兼容的 contracts 实现运行时校验patronum、atomic-router、effector-storage、effector-action、effector-forms、@withease/factories、@withease/i18next、@withease/web-api、@effector/next、@effector/reflect、@withease/redux、eslint-plugin-effector、Effector Babel/SWC 插件该技能不规定项目结构。示例中可能使用 entities/user 或 shared/api 等路径,但不得决定 Feature-Sliced Design(FSD)的分层与切片规则。当问题主要涉及层级划分、切片边界、公共 API 设计、导入限制、文件组织方式或 Effector 项目的整体结构时,请改用 effector-fsd 技能。
目标架构原则如下:
UI 是被动的;业务逻辑是声明式的;远程数据需经运行时校验;启动流程显式且作用域明确;远程操作语义应被建模,而非隐藏在 React 组件中。
当用户提出以下类型的问题时启用该技能:
sample、combine、attach、effects、stores、events、factories、Scope、SSR、scopeBinduseUnit 进行 React 绑定@effector/next、App Router、Pages Router、SSR、hydration、Scope 序列化切勿将本技能作为 FSD 结构的首要依据。有关模块放置与导入边界的决策,请使用 effector-fsd。
作答前,首先识别用户目标:
若用户提供代码,请基于本技能规范进行审查,并返回以下内容:
effector-fsd 同时在上下文中被启用当用户要求“完整审计”、“深度审查”、“架构审计”或“是否全部符合最佳实践?”时,不应止步于局部反模式,而应执行跨切面的静态数据流审计。在撰写最终回答前,请构建并检查以下六类图谱:
main/入口 → fork/Provider → appStarted → 存储/会话恢复 → 路由器/历史记录初始化 → 初始路由打开。标记任何在认证状态未知前即打开受保护路由的路由器/历史记录启动逻辑。401/403 API 错误、路由重定向、受保护路由拒绝访问。标记所有未同步更新路由状态的 sessionCleared/登出/未授权路径。concurrency 配置、abortAll 行为、缓存/更新策略、以及 $pending 门控逻辑。标记重复触发、非预期的 TAKE_EVERY、陈旧结果竞争(stale-result races)、页面模型在其对应路由已关闭时仍尝试重新获取数据等行为。let intervalId)、缺失的 scopeBind、以及已声明但从未被调用的 appDestroyed/stop 事件。sample.fn、combine、store 的 .map、reducer 和 contracts。标记可能抛出异常的转换(如 new Date(...).toISOString()、JSON.parse、不安全属性访问)、过长的内联算法、以及错误地从操作 $data 读取而非从 clock payload 中读取的成功处理器。Promise 的组件、围绕 Effector 事件维护本地 pending 状态的组件、直接发起路由/API 调用的组件、或在 JSX 中执行业务分支逻辑的组件。对每一项发现,需包含:文件/路径证据、严重等级、影响说明、最小修复方案、验收标准。若问题并非直接可见,而是基于 Effector 静态图语义推导得出,请明确标注为“推断风险”,并展示构成该风险的单元链路。
React 组件必须做到:
useUnit 绑定 Effector 单元React 组件禁止:
sample、createEvent、createStore、createEffect 或 factories$store.getState() 读取 store 值watch 实现应用行为逻辑useUnit 形状对于从同一模型中读取/调用多个 Effector 单元的组件,推荐使用单次 useUnit 调用,并以对象或数组形式传入:
const { value, submitDisabled, onValueChange, onSubmit } = useUnit($$form);
或:
const [value, submitDisabled, onValueChange, onSubmit] = useUnit([ $value, $submitDisabled, valueChanged, submitted, ]);
随后解构所有返回值,并将绑定后的回调函数传入 JSX。对于 handler 类型的值,应在 useUnit 返回时使用 React 风格的 on* 别名:暴露 onSubmit: submitted、onValueChange: valueChanged、onRetryClick: retryClicked。Effector 事件本身应在模型中按事实命名;别名仅用于 React 绑定目的。
避免在同一组件中重复调用 useUnit:
// 默认视为不良实践:冗余且在重构时易不同步 const value = useUnit($value); const submitDisabled = useUnit($submitDisabled); const onValueChange = useUnit(valueChangedEvent);
当订阅粒度成为关键考量时,应拆分组件,而非在一个组件中散布多个 useUnit 调用。
仅在模块顶层创建单元。
应使用:
createEvent 表达已发生的事实createStore 表达状态createEffect 仅用于 Farfetched 尚未覆盖的副作用attach 声明式地向 effect 注入 stores/参数sample 作为主要连接操作符combine 构造视图模型(view models)split 或 effector-action 在提升可读性时处理复杂分支combine、sample.fn 或 store .map 中的非平凡数据转换patronum 提供常见操作符:防抖、节流、重置、状态、pending 辅助工具等scopeBind 用于离开 Effector 调用栈但仍需在 Scope 内正常工作的回调应避免:
watch 实现业务逻辑$store.getState()combine、sample.fn 或 store .map 中内联编写过长的映射、排序、分组、归一化或格式化逻辑undefined,除非明确设置了 skipVoid: false优先按关注点划分子模型,而非构建大而全的单一模型。当模型逐渐承载多个业务流程时,应将其拆分为子模型,例如 $$form、$$filters、$$list、$$selection 或 $$dialog;每个子模型应仅负责自身状态与本地规则。顶层模型应保持轻量,仅通过 sample 及其他声明式连接协调子模型之间的交互。
通过将非平凡的数据转换提取为具名纯函数,维持 Effector 模型的声明式特性。简单的布尔判断或字段拼接可保留在内联位置;复杂的映射、排序、分组、DTO 归一化、权限驱动的视图模型、格式化逻辑等,应置于所属模型/API/领域辅助模块附近。模型的作用应是连接 stores/events 并调用这些函数,而非将算法逻辑隐藏在响应式操作符内部。
对于重复出现的表单、筛选器、小部件或其他具备相同行为的独立实例,应优先选用 factories,而非复制粘贴 Effector 模型代码。在 SSR/Scope/SID 敏感型应用中,使用 @withease/factories 并配置 Effector Babel/SWC 插件的 factories 字段;factory 调用必须位于模块顶层,严禁在 render 阶段调用。
使用 Farfetched 处理后端通信:
createJsonQuery 处理数据读取createJsonMutation 处理数据写入declareParams() 提供类型化的参数定义response.contract 实现运行时校验mapData 在校验后映射 DTOmapError: ({ error }) => ... 使用当前 Farfetched 对象参数格式规范化传输层/校验层/领域层错误concurrency 操作符用于路由/搜索/筛选取消;提交去重应优先使用显式的 Effector $pending 门控,谨慎选择 Farfetched concurrencycreateBarrier + applyBarrier 处理 Token 刷新或资源不可用流程keepFresh、cache、.refresh 和 update 在适当时机表达刷新/缓存语义request.fetch.credentials 在当前 Farfetched 代码中用于 Cookie/Session API@farfetched/atomic-router 在使用 Atomic Router 时实现查询驱动的路由加载绝不可在无运行时 contract 的前提下信任后端数据。
优先使用一个应用级事件(如 appStarted)和一个页面/路由级事件(如 pageStarted),而非多个松散的启动函数。
默认模式:
const scope = fork();
await allSettled(appStarted, { scope, params: startParams });
从该事件出发,通过 sample 或 effects 连接存储拾取、i18n 初始化、路由器启动、初始查询及浏览器集成逻辑。默认情况下应避免如下序列:
await startAppClock(scope);
await allSettled(appStarted, { scope });
await startRouter(scope);
该序列默认视为错误。应将适配器安装建模为由 appStarted 启动的 effects(例如 sample({ clock: appStarted, target: routerStartedFx }))。仅在极少数无法用 scoped effect 表达、且必须在首个回调触发前完成的宿主集成边界场景下,才可保留自由浮动的 startAppClock(scope) / startRouter(scope),并需明确文档化。所有业务决策仍必须由 appStarted、路由事件或 scope-bound 回调触发。
仅在你明确需要等待那些在 allSettled(event, { scope }) 调用之外(例如由定时器、SDK、历史监听器或 WebSocket 的 scopeBind 回调)触发的异步任务时,才使用 allSettled(scope)。
不要因流行而引入包,仅在它能解决特定架构问题时才选用。
请参考 references/01-package-map.md 中的包映射关系,以及 references/10-ecosystem-library-notes.md 中的详细说明。
代码示例中可能使用如下路径别名:
@/shared/api/base-url @/entities/session @/features/profile-update @/pages/user/model/page.model
这些仅为示意,不可从中推断完整的项目结构规则。有关模块放置与边界决策,请使用 effector-fsd。
Stores:
export const $user = createStore(null); export const $isAuthorized = createStore(false);
Events:
export const formSubmitted = createEvent(); export const searchChanged = createEvent (); export const pageOpened = createEvent();
useUnit UI 绑定形状应将这些事件暴露为 handler 别名:
export const $$form = {
value: $value,
onValueChange: valueChanged,
onSubmit: formSubmitted,
};
Effects:
export const analyticsTrackedFx = createEffect();
Queries/mutations:
export const userQuery = createJsonQuery(...); export const updateProfileMutation = createJsonMutation(...);
避免模糊命名:
// 不良实践 export const setData = createEvent(); export const update = createEvent(); export const fx = createEffect();
在给出详细回答前,请查阅以下相关文件:
references/00-source-policy.mdreferences/01-package-map.mdreferences/03-effector-modeling.mdreferences/04-farfetched-contracts.mdreferences/05-react-ui-binding.mdreferences/06-routing-forms-persistence-i18n.mdreferences/07-testing-tooling.mdreferences/08-anti-patterns.mdreferences/09-review-checklist.mdreferences/10-ecosystem-library-notes.mdreferences/11-nextjs.mdreferences/12-production-audit-playbook.md力求具体,优先提供可运行的代码示例。
修正代码时,先展示最小正确版本,再解释原因。
当模块放置是核心问题时,应主动切换至或请求启用 effector-fsd。
当某项选择存在权衡时,应说明默认推荐方案及偏离条件。
相关专题
热门下载
相关下载
精品课程
共86课时 | 27.7万人学习
共18课时 | 6.1万人学习
共31课时 | 5万人学习
最新文章