Local MCP Server
在Termux中运行本地MCP服务器,支持Ollama模型的文件读取与命令执行。
下载 11
将 React 类组件迁移至 Hooks,升级至 React 18/19 并引入 TypeScript。
openclaw skills install @wpank/react-modernization命令、参数、文件名以原文为准
将 React 应用从类组件升级为函数式组件,采用并发特性,并在主要版本间进行迁移。
系统化的 React 代码库现代化模式:
react 升级, 类组件转 Hooks, useEffect, useState, react 18, react 19, 并发, Suspense, transition, codemod, 迁移, 现代化, 函数式组件
npx clawhub@latest install react-modernization| 变更 | 影响 | 迁移方式 |
|---|---|---|
| 新的根 API | 必须修改 | ReactDOM.render → createRoot |
| 自动批处理 | 行为变化 | 异步代码中的状态更新现在会被自动批处理 |
| 严格模式 | 仅开发环境 | 效果会执行两次(挂载/卸载/再挂载) |
| 服务端 Suspense | 可选 | 启用 SSR 流式传输 |
| 变更 | 影响 | 迁移方式 |
|---|---|---|
use() Hook | 新增 API | 在渲染中读取 Promise 或上下文 |
ref 作为属性 | 简化语法 | 不再需要 forwardRef |
| 上下文作为提供者 | 简化语法 | <Context> 而非 <Context.Provider> |
| 异步动作 | 新模式 | 使用 useActionState、useOptimistic |
// componentDidMount → useEffect 且依赖为空数组
useEffect(() => {
fetchData()
}, [])
// componentDidUpdate → useEffect 且包含依赖项
useEffect(() => {
updateWhenIdChanges()
}, [id])
// componentWillUnmount → useEffect 的清理函数
useEffect(() => {
const subscription = subscribe()
return () => subscription.unsubscribe()
}, [])
// shouldComponentUpdate → React.memo
const Component = React.memo(({ data }) => <div>{data}</div>)
// getDerivedStateFromProps → useMemo
const derivedValue = useMemo(() => computeFrom(props), [props])// 之前:类组件包含多个状态属性
class UserProfile extends React.Component {
state = { user: null, loading: true, error: null }
componentDidMount() {
fetchUser(this.props.id)
.then(user => this.setState({ user, loading: false }))
.catch(error => this.setState({ error, loading: false }))
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.setState({ loading: true })
fetchUser(this.props.id)
.then(user => this.setState({ user, loading: false }))
}
}
render() {
const { user, loading, error } = this.state
if (loading) return <Spinner />
if (error) return <Error message={error.message} />
return <Profile user={user} />
}
}
// 之后:自定义 Hook + 函数式组件
function useUser(id: string) {
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<Error | null>(null)
useEffect(() => {
let cancelled = false
setLoading(true)
fetchUser(id)
.then(data => {
if (!cancelled) {
setUser(data)
setLoading(false)
}
})
.catch(err => {
if (!cancelled) {
setError(err)
setLoading(false)
}
})
return () => { cancelled = true }
}, [id])
return { user, loading, error }
}
function UserProfile({ id }: { id: string }) {
const { user, loading, error } = useUser(id)
if (loading) return <Spinner />
if (error) return <Error message={error.message} />
return <Profile user={user} />
}// 之前:高阶组件
function withUser(Component) {
return function WithUser(props) {
const [user, setUser] = useState(null)
useEffect(() => { fetchUser().then(setUser) }, [])
return <Component {...props} user={user} />
}
}
const ProfileWithUser = withUser(Profile)
// 之后:自定义 Hook(更简洁,可组合)
function useCurrentUser() {
const [user, setUser] = useState(null)
useEffect(() => { fetchUser().then(setUser) }, [])
return user
}
function Profile() {
const user = useCurrentUser()
return user ? <div>{user.name}</div> : null
}// 之前:React 17
import ReactDOM from 'react-dom'
ReactDOM.render(<App />, document.getElementById('root'))
// 之后:React 18+
import { createRoot } from 'react-dom/client'
const root = createRoot(document.getElementById('root')!)
root.render(<App />)function SearchResults() {
const [query, setQuery] = useState('')
const [results, setResults] = useState([])
const [isPending, startTransition] = useTransition()
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
// 紧急:立即更新输入框
setQuery(e.target.value)
// 非紧急:可以被中断
startTransition(() => {
setResults(searchDatabase(e.target.value))
})
}
return (
<>
<input value={query} onChange={handleChange} />
{isPending ? <Spinner /> : <ResultsList data={results} />}
</>
)
}// React 19 的 use() 钩子
function ProfilePage({ userId }: { userId: string }) {
return (
<Suspense fallback={<ProfileSkeleton />}>
<ProfileDetails userId={userId} />
</Suspense>
)
}
function ProfileDetails({ userId }: { userId: string }) {
// use() 会暂停,直到 Promise 解析完成
const user = use(fetchUser(userId))
return <h1>{user.name}</h1>
}// 在渲染中直接读取 Promise
function Comments({ commentsPromise }) {
const comments = use(commentsPromise)
return comments.map(c => <Comment key={c.id} {...c} />)
}
// 读取上下文(比 useContext 更简单)
function ThemeButton() {
const theme = use(ThemeContext)
return <button className={theme}>点击</button>
}// useActionState 用于表单提交
function UpdateName() {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get('name'))
if (error) return error
redirect('/profile')
},
null
)
return (
<form action={submitAction}>
<input name="name" />
<button disabled={isPending}>更新</button>
{error && <p>{error}</p>}
</form>
)
}# 更新到新的 JSX 转换(无需 React 导入)
npx codemod@latest react/19/replace-reactdom-render
# 更新已弃用的 API
npx codemod@latest react/19/replace-string-ref
# 类组件转函数组件
npx codemod@latest react/19/replace-use-form-state# 查找类组件
rg "class \w+ extends (React\.)?Component" --type tsx
# 查找已弃用的生命周期方法
rg "componentWillMount|componentWillReceiveProps|componentWillUpdate" --type tsx
# 查找 ReactDOM.render(需要迁移到 createRoot)
rg "ReactDOM\.render" --type tsx// 为函数式组件添加类型
interface ButtonProps {
onClick: () => void
children: React.ReactNode
variant?: 'primary' | 'secondary'
}
function Button({ onClick, children, variant = 'primary' }: ButtonProps) {
return (
<button onClick={onClick} className={variant}>
{children}
</button>
)
}
// 类型化事件处理器
function Form() {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value)
}
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
</form>
)
}
// 泛型组件
interface ListProps<T> {
items: T[]
renderItem: (item: T) => React.ReactNode
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <>{items.map(renderItem)}</>
}useStateuseEffectcreateRoot API// eslint-disable-next-line react-hooks/exhaustive-deps已收录 3 个 Skill