我们可以通过 props 将函数传递给子组件。
如果这个函数中,返回的是 JSX,那么该函数就是一个正常的 React 组件定义。例如在一个列表组件中,我们可以通过 renderItem
属性来传递一个函数,该函数返回一个 JSX 元素,该元素就是列表中的一个子元素。
1<List2renderItem={(item) => {3return <ListItem>{item.name}</ListItem>4}}5/>
此时,这种传递,我们就可用术语 render props
来描述。render props 是一种在 React 组件之间,使用值为函数的 prop 共享代码的简单技术。具有 render props 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑。
1<DataProvider render={data => (2<h1>Hello {data.target}</h1>3)}/>
对于新手朋友来说,这个概念理解起来有点困难,我们下面通过更多的例子来说明它的使用场景。
render props 的合理使用时机非常难把控。因此这篇文章的案例值得多看几遍。
我们需要特别注意的是,此时传入的是一个函数,该函数返回的是一个 JSX 元素,因此该函数就是一个 React 组件。
那么在组件内部,我们就可以通过 props.renderItem
来获取该函数,然后调用该函数,获取返回的 JSX 元素,并将其渲染出来。
props.renderItem({ name: 'Hello' })
由此我们需要考虑的时,什么情况下,使用 render props 是合适的。注意以下几个点
我们通常使用 render props 来实现包含状态逻辑片段的复用。或者有的地方称为横切关注点(Cross-Cutting Concerns)
下面来一起实现一个简单的 Mouse 组件来说明这个问题。
我有一个需求,是可以随时获得到鼠标的位置信息。但是,我就试图通过一种方式,将获取鼠标位置信息的能力,通过 React 组件封装起来可以复用
那么我们应该怎么做呢?
方法很简单
首先,我们定义一个 Mouse 组件,该组件会监听鼠标的位置,并在组件内部维护鼠标的位置信息。当外部有函数组件传入时,我们调用该函数,并将其传递给外部组件。
10import { useState, useEffect } from 'react';2030interface Position {40x: number,50y: number60}7080interface MouseProps {90render: (position: Position) => React.ReactNode10}1112export default function Mouse(props: MouseProps) {13const [position, setPosition] = useState({ x: 0, y: 0 })1415useEffect(() => {16const handleMouseMove = (event: MouseEvent) => {17setPosition({ x: event.clientX, y: event.clientY })18}1920window.addEventListener('mousemove', handleMouseMove)2122return () => {23window.removeEventListener('mousemove', handleMouseMove)24}25}, [])2627return props.render(position)28}
这样,我们就把获取鼠标位置信息的能力,提取到了一个 Mouse 组件中。当我们想要使用时,只需要将一个函数组件作为 prop 传递给 Mouse 组件即可。
完整代码如下
10import Mouse from './mouse'2030export default function App() {40return (50<div className='flex items-center gap-4'>60<Mouse render={({ x, y }) => (70<div>80<h1>Mouse Position:</h1>90<p>X: {x}</p>10<p>Y: {y}</p>11</div>12)} />13</div>14)15}16
在过去 class 的组件开发年代,render props 是重要的状态封装手段。是必须掌握的一个高阶技能。随着 React Hooks 的兴起,render props 的应用场景逐渐被 Hooks 取代。
render props 被取代的主要原因,是因为我们想要封装的仅仅是包含状态的逻辑片段,但 render props 必须被处理成一个 React 组件。这会严重增加 Fiber Tree 结构的复杂性,从而让更新 diff 的工作量变得更大。
不过在许多项目的封装中,依然有一大批使用 render props 的代码存在。因此,他可能不再是我们实现新功能的首选,但是认识并理解它,依然是一件非常有必要的事情。在后续的学习中,我们会进一步学习 React Hooks 的知识以在代码封装中取代 render props 的地位。