0Pricing
Frontend Academy · 课时

渲染属性与 HOC 模式

将渲染函数作为属性传入以注入界面,并用高阶组件包装组件,在不修改原组件的情况下注入行为。

渲染属性与 HOC 模式 是 CoddyKit 上的免费 Frontend Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Frontend Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Frontend Academy 课程共包含 4 节课。

两种经典复用模式

在钩子出现之前(React 16.8),复用有状态逻辑意味着使用高阶组件(HOC)或渲染属性二者之一。它们在新代码中很少见,但您仍会在遗留代码库和许多库中遇到它们。

渲染属性——基本概念

组件接收一个作为属性的函数(通常命名为 render 或 children),并使用内部状态调用它。使用者决定如何利用该状态进行渲染。

function Mouse({ render }) {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  useEffect(() => {
    const handler = (e) => setPos({ x: e.clientX, y: e.clientY });
    window.addEventListener('mousemove', handler);
    return () => window.removeEventListener('mousemove', handler);
  }, []);
  return render(pos);
}

// Usage:
<Mouse render={({ x, y }) => <p>{x}, {y}</p>} />

作为函数的 children

children 属性也可以是一个函数,这是同一种模式,但 JSX 更简洁。

function Mouse({ children }) {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  /* ... */
  return children(pos);
}

<Mouse>
  {({ x, y }) => <p>{x}, {y}</p>}
</Mouse>

高阶组件——基本概念

HOC 是一个接收组件并返回新组件的函数,新组件会获得额外属性或行为。它曾被广泛用于 Redux 的 connect() 和 React Router 的 withRouter()。

function withMouse(Component) {
  return function WithMouse(props) {
    const [pos, setPos] = useState({ x: 0, y: 0 });
    useEffect(() => {
      const handler = (e) => setPos({ x: e.clientX, y: e.clientY });
      window.addEventListener('mousemove', handler);
      return () => window.removeEventListener('mousemove', handler);
    }, []);
    return <Component {...props} mouse={pos} />;
  };
}

// Usage:
const Position = ({ mouse }) => <p>{mouse.x}, {mouse.y}</p>;
const EnhancedPosition = withMouse(Position);
<EnhancedPosition />

高阶组件通过包装进行组合

您可以叠加多个 HOC,但它们会形成难以调试的“包装地狱”。

// Multiple HOCs:
const Enhanced = withRouter(connect(mapState)(withTranslation()(MyComponent)));
// Equivalent in modern React:
// const Enhanced = MyComponent (reads from hooks: useRouter, useSelector, useTranslation)

渲染属性与 HOC

渲染属性:更明确、更适合类型检查,并且没有命名冲突。HOC:更具声明性;如果不谨慎,被包装组件会丢失静态类型,也可能出现属性名称冲突。

为什么钩子取代了两者

自定义钩子以更少的形式化代码实现相同目标:不需要包装组件、不需要嵌套的函数参数,也不需要复杂的类型技巧。钩子是 2025 年惯用的解决方案。

// Custom hook equivalent of useMouse:
function useMouse() {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  useEffect(() => {
    const handler = (e) => setPos({ x: e.clientX, y: e.clientY });
    window.addEventListener('mousemove', handler);
    return () => window.removeEventListener('mousemove', handler);
  }, []);
  return pos;
}

// Usage:
const { x, y } = useMouse();

渲染属性仍然适用的场景

对于需要注入标记(而不只是状态)的组件,例如在特定位置渲染项目的虚拟化列表,渲染属性仍然是自然的选择。

function VirtualList({ items, itemHeight, renderItem }) {
  const [scrollTop, setScrollTop] = useState(0);
  const start = Math.floor(scrollTop / itemHeight);
  const visible = items.slice(start, start + 20);
  return visible.map((item, i) => renderItem(item, start + i));
}

<VirtualList items={data} itemHeight={40} renderItem={(item, i) => (
  <Row key={i} data={item} />
)} />

HOC 最佳实践(必须使用时)

如果必须编写 HOC:请复制静态方法(hoist-non-react-statics),转发引用(React.forwardRef),并为 DevTools 保留 displayName。

在 HOC 中转发引用

HOC 默认会隐藏引用,请使用 forwardRef,以便使用者仍能访问底层组件的 ref。

function withLogger(Component) {
  const Wrapped = React.forwardRef((props, ref) => {
    console.log('rendering', Component.name);
    return <Component {...props} ref={ref} />;
  });
  Wrapped.displayName = `withLogger(${Component.name})`;
  return Wrapped;
}

识别遗留模式

您会在以下位置遇到 HOC 或渲染属性:react-redux(较旧的 API)、Apollo Client v2、react-router v5(withRouter)、Formik(Field)、styled-components(withTheme)。当这些库的新 API 支持时,请迁移到钩子。

快速检查

钩子主要取代了哪种用于在 React 组件之间共享有状态逻辑的模式?

回顾:渲染属性与 HOC

渲染属性:作为属性的函数,返回 JSX,明确且适合类型检查。HOC:包装组件的函数,旧版库会使用它(connect、withRouter)。自定义钩子取代了两者来处理状态逻辑。当需要注入自定义标记(例如虚拟化列表)时,渲染属性仍然更适用。编写 HOC 时:使用 forwardRef、提升静态属性,并设置 displayName。

常见问题解答

「渲染属性与 HOC 模式」课时是免费的吗?

是的 — 「渲染属性与 HOC 模式」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Frontend Academy 课程的其余内容,请升级到 CoddyKit PRO。 Frontend Academy 课程共包含 4 节课。

「渲染属性与 HOC 模式」这节课中我会学到什么?

将渲染函数作为属性传入以注入界面,并用高阶组件包装组件,在不修改原组件的情况下注入行为。 你通过在浏览器中直接运行的动手代码来练习 Frontend Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Frontend Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Frontend Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「渲染属性与 HOC 模式」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Frontend Academy 课中编写并运行代码吗?

能。每节 Frontend Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 Context 的复合组件
  2. 渲染属性与 HOC 模式
  3. 用于模态框和工具提示的 Portal
  4. 错误边界
← 返回 Frontend Academy