mcpField
R
react-dev

react-dev

github.com/fixmyberlin开发065 次安装3 天前更新
代码仓库

关于 react-dev

FixMyBerlin/FMC 的 React 与 TypeScript 组件规范技能,重点涵盖类型编写和审慎使用 `useEffect`。面向编写或审查 React 代码的开发者,并将路由及服务端工作指向相关技能。

主要功能

  1. 01React 19 组件与 ref 类型
  2. 02为 `useEffect` 函数命名
  3. 03何时避免使用 Effect 的指引
  4. 04React Compiler 与 Oxlint 约定
  5. 05React 19 API 使用说明

适用场景

  1. 01为组件属性和事件处理器添加类型
  2. 02审查或编写 Effect 逻辑
  3. 03避免用 Effect 处理派生状态或数据获取
  4. 04遵循 React Compiler 与 lint 约定

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/fixmyberlin/fixmyskills --skill react-dev

安装后在 Claude Code 中输入/react-dev确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

examples/
generic-components.md
references/
effects.md
event-handlers.md
react-19-patterns.md
tanstack-router.md

详细介绍

LLM 参考资料: react.dev/llms.txt · tanstack.com/llms.txt · 关键 effect 页面:你可能不需要 Effect

React + TypeScript (FMC)

FMC 关于 React 类型定义和 useEffect 使用规范(命名、作用域、何时跳过)的约定。路由和服务端 I/O 由相关技能负责(见下文)。

FMC 技术栈(必需)

要求规则
React 19目标版本为 19.x;新代码中不得使用 forwardRef / useFormState / useActionState;表单使用 TanStack Form
React Compiler在应用构建中启用 — 默认自动记忆化
LintOxlint react 插件 — 默认启用原生 Compiler 规则,另加 'react/unsupported-syntax': 'error';不使用 eslint-plugin-react-hooks jsPlugin
记忆化默认不要添加 useMemo / useCallback / memo;仅在性能分析或 lint 规则要求时添加
TanStack Start服务端 I/O 和变更操作 → tanstack-start-conventions(本技能仅涵盖组件类型定义)
数据获取路由加载器 + React Query — 不使用 useEffect 获取数据(参见 tanstack-router-conventions)

文档:React Compiler · React 规则 · Oxc React Compiler 支持

相关技能

主题技能
路由、加载器、validateSearchtanstack-router-conventions
URL 状态(优先使用路由搜索参数)tanstack-router-conventions
SSR、服务端函数、Start 布局tanstack-start-conventions
文件夹布局、精简路由(Start)技能 tanstack-start-conventions → references/app-structure.md
createServerFn、服务端变更操作技能 tanstack-start-conventions → references/server-functions.md
客户端存储(不要使用 context + reducer)zustand-state-management
语义化 HTML(遗留的 div/span)技能 unslop-code → references/semantic-html.md

类型标注说明

  • Props: 使用 React.ComponentPropsWithoutRef<'button'> 扩展原生元素(不包含 ref);使用 children: React.ReactNode,不要使用 JSX.Element。
  • 变体 props: 使用判别联合({ variant: 'link'; href: string } | { variant: 'button'; onClick: () => void }),并根据判别字段缩小类型范围。
  • ref: 在 React 19 中是普通 prop,不需要新增 forwardRef → react-19-patterns.md。
  • 事件: 使用具体的 React.*Event<HTMLElement> 类型,以便为 currentTarget 提供类型;不要使用 any → event-handlers.md。
  • Hooks: 对 null、[] 和联合类型,显式使用 useState<User | null>(null);对 DOM 使用 useRef<HTMLInputElement>(null) + ?.,对可变容器使用 useRef(0);对 useReducer 操作使用判别联合;使用 createContext<T | null>(null) + 会抛出异常的自定义 hook。外部存储:useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)。
  • 路由 hooks: 在 createFileRoute 组件内使用 Route.useParams() / Route.useSearch();validateSearch 放在路由文件中 → tanstack-router.md。

useEffect——命名、作用范围以及何时跳过

Effect 是用于与外部系统(非 React 小组件、浏览器 API、地图 SDK、订阅、分析)同步的备用方案。如果不涉及外部系统,通常就不应使用 Effect。

为每个 useEffect 命名

始终向 useEffect 传入具名函数表达式,不要使用匿名箭头函数。这是新代码和修改后代码的默认风格,包括自定义 hooks 内的 Effect。也要为非简单的清理函数命名。

useEffect(
  function updateDocumentTitle() {
    document.title = `${count} items`
  },
  [count],
)

useEffect(function pollServerForUpdates() {
  const intervalId = setInterval(/* ... */, 5000);
  return function stopPollingServer() {
    clearInterval(intervalId);
  };
}, [serverId]);
从命名中发现问题

在键盘前审查设计时,命名就是一个信号。详情请参阅:effects.md。

信号含义修正
名称需要用到 “and” / “also”一个 Effect 中包含无关的职责拆分为多个 Effect,每个 Effect 使用一个名称
syncDerivedValue、updateStateFromState、setXBasedOnY派生状态或状态之间的同步在渲染期间派生
notifyParentOfChange、reportStateToParent根据子组件状态更新父组件在事件处理函数中调用、向上提升数据,或将组件设为受控组件
resetFormOnSubmitFlag通过状态跳转表达用户意图在事件处理函数中处理
persistOnClose、syncWhenClosed、saveDraftOnDismiss关闭时产生的副作用/打开状态的 UI使用处理函数,而不是监听 isOpen 的 Effect(effects.md §3)
明确的外部动词:connectTo…、subscribeTo…、initialize…、synchronize…(浏览器、网络、地图 SDK)通常属于合理的 Effect 范畴仍应为它们命名;需要时添加清理逻辑

如果诚实的命名听起来像是在处理 React 内部事务,那么这段代码就应该放在渲染过程中、事件处理函数中,或改用其他模式。

快速参考
情况不要这样做应该这样做
从 props/state 派生状态useState + useEffect在渲染期间计算(默认不使用 useMemo,Compiler 已启用)
在 prop 变化时重置状态使用 useEffect 和 setState使用 key prop
响应用户事件使用 useEffect 监听状态直接在事件处理程序中处理
关闭时持久化/同步使用 useEffect 监听 isOpen使用关闭处理程序(或 onCloseComplete)
通知父组件发生变化使用 useEffect 调用 onChange在事件处理程序中调用,或使用受控组件
从子组件向父组件传递数据使用 Effect 调用 onFetched由父组件获取数据并向下传递
外部存储使用 Effect + useState使用 useSyncExternalStore
获取数据使用 useEffect使用路由加载器 + React Query

Strict Mode(开发环境): 挂载时会运行两次 Effect 的设置逻辑。为订阅和计时器添加清理逻辑;对仅需初始化一次的操作进行保护(模块级 didInit 守卫,参见 effects.md §6)。

Effects 与 React Compiler / lint
  • Compiler 不会取代用于外部同步的 Effects。记忆化方式的变化可能改变 Effect 重新运行的时机——应修正 Effect 的设计(依赖项、拆分、useEffectEvent),而不是禁用 Compiler。
  • Lint:Oxlint 原生 React Compiler 规则(react/set-state-in-effect、react/immutability,……)。
useEffectEvent

适用于必须保持订阅(连接、间隔、监听器)的 Effect,同时回调中的一部分会读取最新的 props/state。示例:effects.md §7。

  • 使用类似用户可见事件的名称:onConnected、onTick,不要用 onMount / onUpdate。
  • 只能从 Effects(或同一组件中的其他 Effect Events)调用;不要在渲染期间、事件处理程序中调用,也不要作为 prop 传给子组件。
  • 从依赖数组中省略(由 lint 强制);绝不要用它隐藏本应触发 Effect 重新运行的依赖项。

React 19 API(参考)

API用途文档
use()解包 promise/context(可能触发挂起)use
useOptimistic乐观 UIuseOptimistic
useTransition非紧急更新useTransition
useEffectEventEffect 内的稳定回调useEffectEvent

规则

始终

  • 使用命名的 useEffect 函数;启用 React Compiler;使用 oxlint 原生 react/* Compiler 规则
  • 优先使用语义化 HTML,而非通用的 div / span — skill unslop-code → references/semantic-html.md
  • 在 React 19 中将 ref 作为 prop

绝不

  • 新增 forwardRef / useFormState / useActionState(表单:TanStack Form)
  • 无充分理由时默认使用 useMemo / useCallback / memo
  • 使用 useEffect 获取应用数据(改用 loaders/Query)
  • 等待传给 use() 的 promise 完成,因为要将其交给流式传输

参考资料

  • effects.md — 编写或审查 useEffect 时,或效果名称命中上方的异味表时阅读(使用 key 重置、手势触发后持久化、父级数据流、初始化保护、useEffectEvent 示例)
  • event-handlers.md — 为点击/提交/更改以外的事件处理器指定类型,或通过属性转发事件时阅读
  • react-19-patterns.md — 将 ref 作为属性、使用 use(),或将旧代码库迁移到 React 19 时阅读
  • generic-components.md — 编写泛型组件时阅读(受约束的 T extends HasId、带访问器的 Select<T>)
  • tanstack-router.md — 为路由钩子指定类型时阅读(权威参考:tanstack-router-conventions → router-typescript.md)
  • Neciu Dan — 开始为你的 useEffect 函数命名

评论

支持 Markdown