mcpField
R
react-modernization

react-modernization

github.com/wshobson开发41.9k12.8k 次安装2 周前更新
代码仓库

关于 react-modernization

这项技能介绍如何通过版本升级、类组件迁移到 Hooks 以及采用 React 18 功能来现代化 React 应用,面向维护或重构 React 项目的开发者。

主要功能

  1. 01React 版本升级指导
  2. 02类组件迁移到 Hooks
  3. 03React 18 并发功能
  4. 04借助 Codemod 重构
  5. 05状态管理模式现代化

适用场景

  1. 01升级现有 React 应用
  2. 02将类组件替换为 Hooks
  3. 03采用 Suspense 和 transitions
  4. 04更新项目模式以使用 TypeScript

安装

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

$npx skills add https://github.com/wshobson/agents --skill react-modernization

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

附带文件references/details.md

详细介绍

React 现代化

掌握 React 版本升级、类组件到 Hook 的迁移、并发特性的采用,以及用于自动化转换的 codemod。

何时使用此技能

  • 将 React 应用升级到最新版本
  • 将类组件迁移到带 Hook 的函数组件
  • 采用 React 并发特性(Suspense、过渡)
  • 应用 codemod 进行自动化重构
  • 现代化状态管理模式
  • 升级到 TypeScript
  • 利用 React 18+ 特性提升性能

版本升级路径

React 16 → 17 → 18

各版本的破坏性变更:

React 17:

  • 事件委托变更
  • 移除事件池化
  • 副作用清理时机
  • JSX 转换(无需导入 React)

React 18:

  • 自动批处理
  • 并发渲染
  • Strict Mode 变更(双重调用)
  • 新的 root API
  • 服务端 Suspense

类组件到 Hook 的迁移

状态管理
// Before: Class component
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      name: "",
    };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

// After: Functional component with hooks
function Counter() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("");

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}
生命周期方法到 Hook
// Before: Lifecycle methods
class DataFetcher extends React.Component {
  state = { data: null, loading: true };

  componentDidMount() {
    this.fetchData();
  }

  componentDidUpdate(prevProps) {
    if (prevProps.id !== this.props.id) {
      this.fetchData();
    }
  }

  componentWillUnmount() {
    this.cancelRequest();
  }

  fetchData = async () => {
    const data = await fetch(`/api/${this.props.id}`);
    this.setState({ data, loading: false });
  };

  cancelRequest = () => {
    // Cleanup
  };

  render() {
    if (this.state.loading) return <div>Loading...</div>;
    return <div>{this.state.data}</div>;
  }
}

// After: useEffect hook
function DataFetcher({ id }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let cancelled = false;

    const fetchData = async () => {
      try {
        const response = await fetch(`/api/${id}`);
        const result = await response.json();

        if (!cancelled) {
          setData(result);
          setLoading(false);
        }
      } catch (error) {
        if (!cancelled) {
          console.error(error);
        }
      }
    };

    fetchData();

    // Cleanup function
    return () => {
      cancelled = true;
    };
  }, [id]); // Re-run when id changes

  if (loading) return <div>Loading...</div>;
  return <div>{data}</div>;
}
Context 和 HOC 到 Hook
// Before: Context consumer and HOC
const ThemeContext = React.createContext();

class ThemedButton extends React.Component {
  static contextType = ThemeContext;

  render() {
    return (
      <button style={{ background: this.context.theme }}>
        {this.props.children}
      </button>
    );
  }
}

// After: useContext hook
function ThemedButton({ children }) {
  const { theme } = useContext(ThemeContext);

  return <button style={{ background: theme }}>{children}</button>;
}

// Before: HOC for data fetching
function withUser(Component) {
  return class extends React.Component {
    state = { user: null };

    componentDidMount() {
      fetchUser().then((user) => this.setState({ user }));
    }

    render() {
      return <Component {...this.props} user={this.state.user} />;
    }
  };
}

// After: Custom hook
function useUser() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser().then(setUser);
  }, []);

  return user;
}

function UserProfile() {
  const user = useUser();
  if (!user) return <div>Loading...</div>;
  return <div>{user.name}</div>;
}

React 18 并发特性

新的 root API
// Before: React 17
import ReactDOM from "react-dom";

ReactDOM.render(<App />, document.getElementById("root"));

// After: React 18
import { createRoot } from "react-dom/client";

const root = createRoot(document.getElementById("root"));
root.render(<App />);
自动批处理
// React 18: All updates are batched
function handleClick() {
  setCount((c) => c + 1);
  setFlag((f) => !f);
  // Only one re-render (batched)
}

// Even in async:
setTimeout(() => {
  setCount((c) => c + 1);
  setFlag((f) => !f);
  // Still batched in React 18!
}, 1000);

// Opt out if needed
import { flushSync } from "react-dom";

flushSync(() => {
  setCount((c) => c + 1);
});
// Re-render happens here
setFlag((f) => !f);
// Another re-render
过渡
import { useState, useTransition } from "react";

function SearchResults() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);
  const [isPending, startTransition] = useTransition();

  const handleChange = (e) => {
    // Urgent: Update input immediately
    setQuery(e.target.value);

    // Non-urgent: Update results (can be interrupted)
    startTransition(() => {
      setResults(searchResults(e.target.value));
    });
  };

  return (
    <>
      <input value={query} onChange={handleChange} />
      {isPending && <Spinner />}
      <Results data={results} />
    </>
  );
}
用于数据获取的 Suspense
import { Suspense } from "react";

// Resource-based data fetching (with React 18)
const resource = fetchProfileData();

function ProfilePage() {
  return (
    <Suspense fallback={<Loading />}>
      <ProfileDetails />
      <Suspense fallback={<Loading />}>
        <ProfileTimeline />
      </Suspense>
    </Suspense>
  );
}

function ProfileDetails() {
  // This will suspend if data not ready
  const user = resource.user.read();
  return <h1>{user.name}</h1>;
}

function ProfileTimeline() {
  const posts = resource.posts.read();
  return <Timeline posts={posts} />;
}

更多模式和模板

更详细的模板和实践示例位于 references/details.md。阅读该文件以获取完整的模式库。

评论

支持 Markdown