Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: translation render props #106

Merged
merged 16 commits into from
Mar 27, 2019
105 changes: 52 additions & 53 deletions content/docs/render-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ title: Render Props
permalink: docs/render-props.html
---

The term ["render prop"](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce) refers to a technique for sharing code between React components using a prop whose value is a function.
术语 ["render prop"](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce) 是指一种在 React 组件之间使用一个值为函数的 prop 在 React 组件间共享代码的简单技术
LeoEatle marked this conversation as resolved.
Show resolved Hide resolved

A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.
具有 render prop 的组件接受一个函数,该函数返回一个 React 元素并调用它而不是实现自己的渲染逻辑。

```jsx
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>
```

Libraries that use render props include [React Router](https://reacttraining.com/react-router/web/api/Route/Route-render-methods) and [Downshift](https://github.com/paypal/downshift).
使用 render prop 的库有 [React Router](https://reacttraining.com/react-router/web/api/Route/Route-render-methods) [Downshift](https://github.com/paypal/downshift).

In this document, we’ll discuss why render props are useful, and how to write your own.
在这个文档中,我们将讨论为什么 render prop 是有用的,以及如何写一个自己的 render prop 组件。

## Use Render Props for Cross-Cutting Concerns {#use-render-props-for-cross-cutting-concerns}
## 在横切关注点(Cross-Cutting Concerns)使用 render prop {#use-render-props-for-cross-cutting-concerns}
LeoEatle marked this conversation as resolved.
Show resolved Hide resolved

Components are the primary unit of code reuse in React, but it's not always obvious how to share the state or behavior that one component encapsulates to other components that need that same state.
组件是在 React 主要的代码复用单元,但如何共享状态或一个组件的行为封装到其他需要相同 state 的组件中却不是显而易见的。
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

For example, the following component tracks the mouse position in a web app:
例如,以下组件跟踪 Web 应用程序中的鼠标位置:

```js
class MouseTracker extends React.Component {
Expand All @@ -42,22 +42,22 @@ class MouseTracker extends React.Component {
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
<h1>Move the mouse around!</h1>
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
<h1>移动鼠标!</h1>
<p>当前的鼠标位置是 ({this.state.x}, {this.state.y})</p>
</div>
);
}
}
```

As the cursor moves around the screen, the component displays its (x, y) coordinates in a `<p>`.
当光标在屏幕上移动时,组件在 `<p>` 中显示其(x,y)坐标。

Now the question is: How can we reuse this behavior in another component? In other words, if another component needs to know about the cursor position, can we encapsulate that behavior so that we can easily share it with that component?
现在的问题是:我们如何在另一个组件中重用这个行为?换个说法,若另一个组件需要知道鼠标位置,我们能否封装这一行为让它可以在其他组件间轻松的共享?
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

Since components are the basic unit of code reuse in React, let's try refactoring the code a bit to use a `<Mouse>` component that encapsulates the behavior we need to reuse elsewhere.
由于组件是 React 中最基础的代码重用单元,现在尝试重构一部分代码使其能够在 `<Mouse>` 组件中封装我们需要共享的行为。
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

```js
// The <Mouse> component encapsulates the behavior we need...
// <Mouse> 组件封装了我们需要的行为...
class Mouse extends React.Component {
constructor(props) {
super(props);
Expand All @@ -76,7 +76,7 @@ class Mouse extends React.Component {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>

{/* ...but how do we render something other than a <p>? */}
{/* ...但我们如何渲染 <p> 以外的东西? */}
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
</div>
);
Expand All @@ -87,19 +87,19 @@ class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<h1>移动鼠标!</h1>
<Mouse />
</div>
);
}
}
```

Now the `<Mouse>` component encapsulates all behavior associated with listening for `mousemove` events and storing the (x, y) position of the cursor, but it's not yet truly reusable.
现在 `<Mouse>` 组件封装了所有关于监听 `mousemove` 事件和存储鼠标 (x, y) 位置的行为,但其仍不是真正的可重用。

For example, let's say we have a `<Cat>` component that renders the image of a cat chasing the mouse around the screen. We might use a `<Cat mouse={{ x, y }}>` prop to tell the component the coordinates of the mouse so it knows where to position the image on the screen.
举个例子, 让我们假设现在有一个在屏幕上跟随鼠标渲染一张猫的图片的 `<Cat>` 组件。我们可能使用 `<Cat mouse={{ x, y }}` prop 来告诉组件鼠标的坐标以让它知道图片应该在屏幕哪个位置。
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

As a first pass, you might try rendering the `<Cat>` *inside `<Mouse>`'s `render` method*, like this:
首先, 你可能会像这样,尝试在 `<Mouse>` 的内部的渲染方法渲染 `<Cat>` 组件::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

「的内部」->「内部」


```js
class Cat extends React.Component {
Expand Down Expand Up @@ -130,10 +130,9 @@ class MouseWithCat extends React.Component {
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>

{/*
We could just swap out the <p> for a <Cat> here ... but then
we would need to create a separate <MouseWithSomethingElse>
component every time we need to use it, so <MouseWithCat>
isn't really reusable yet.
我们可以在这里换掉<p>的<Cat> ......
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved
但是接着我们需要创建一个单独的<MouseWithSomethingElse>
每次我们需要使用它时,<MouseWithCat> 是不是真的可以重复使用.
*/}
<Cat mouse={this.state} />
</div>
Expand All @@ -145,17 +144,17 @@ class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<h1>移动鼠标!</h1>
<MouseWithCat />
</div>
);
}
}
```

This approach will work for our specific use case, but we haven't achieved the objective of truly encapsulating the behavior in a reusable way. Now, every time we want the mouse position for a different use case, we have to create a new component (i.e. essentially another `<MouseWithCat>`) that renders something specifically for that use case.
这种方法适用于我们的特定用例,但我们还没有达到以可重用的方式真正封装行为的目标。现在,每当我们想要鼠标位置用于不同的用例时,我们必须创建一个新的组件(本质上是另一个 `<MouseWithCat>` ),它专门为该用例呈现一些东西.

Here's where the render prop comes in: Instead of hard-coding a `<Cat>` inside a `<Mouse>` component, and effectively changing its rendered output, we can provide `<Mouse>` with a function prop that it uses to dynamically determine what to render–a render prop.
这也是 render prop 的来历:我们可以提供一个带有函数 prop 的 `<Mouse>` 组件,它能够动态决定什么需要渲染的,而不是将 `<Cat>` 硬编码到 `<Mouse>` 组件里,并有效地改变它的渲染结果。

```js
class Cat extends React.Component {
Expand Down Expand Up @@ -199,7 +198,7 @@ class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<h1>移动鼠标!</h1>
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
Expand All @@ -209,17 +208,17 @@ class MouseTracker extends React.Component {
}
```

Now, instead of effectively cloning the `<Mouse>` component and hard-coding something else in its `render` method to solve for a specific use case, we provide a `render` prop that `<Mouse>` can use to dynamically determine what it renders.
现在,我们提供了一个 render prop 以让 <Mouse> 能够动态决定什么需要渲染,而不是克隆 <Mouse> 组件并硬编码来解决特定的用例。
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

More concretely, **a render prop is a function prop that a component uses to know what to render.**
更具体地说, **render prop 是一个组件用来知道要渲染什么内容的函数 prop**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

「组件用来知道要渲染什么内容」->「用于告知组件需要渲染什么内容」


This technique makes the behavior that we need to share extremely portable. To get that behavior, render a `<Mouse>` with a `render` prop that tells it what to render with the current (x, y) of the cursor.
这一技术使得共享代码间变得相当容易。要获得这个行为,只要渲染一个带有 render prop 的 `<Mouse>` 组件就能够告诉它当前鼠标坐标 (x, y) 要渲染什么。
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

One interesting thing to note about render props is that you can implement most [higher-order components](/docs/higher-order-components.html) (HOC) using a regular component with a render prop. For example, if you would prefer to have a `withMouse` HOC instead of a `<Mouse>` component, you could easily create one using a regular `<Mouse>` with a render prop:
关于 render prop 的一个有趣的事情是你可以使用带有 render prop 的常规组件来实现大多数[高阶组件](/docs/higher-order-components.html) (HOC)。 例如,如果你更喜欢使用 `withMouse` HOC而不是 `<Mouse>` 组件,你可以使用带有 render prop 的常规 `<Mouse>` 轻松创建一个:

```js
// If you really want a HOC for some reason, you can easily
// create one using a regular component with a render prop!
// 如果您出于某种原因真的想要HOC,那么您可以轻松实现
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved
// 使用具有 render prop 的常规组件创建一个!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

「常规」换成「普通」会不会更好些?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

常规感觉更官方一点,有种教程的感觉

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同觉得普通好一点...常规组件有点难以定义...

function withMouse(Component) {
return class extends React.Component {
render() {
Expand All @@ -233,51 +232,51 @@ function withMouse(Component) {
}
```

So using a render prop makes it possible to use either pattern.
因此,您可以将任一模式与 render prop 一起使用。

## Using Props Other Than `render` {#using-props-other-than-render}
## 使用 Props 而非 render {#using-props-other-than-render}
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

It's important to remember that just because the pattern is called "render props" you don't *have to use a prop named `render` to use this pattern*. In fact, [*any* prop that is a function that a component uses to know what to render is technically a "render prop"](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce).
重要的是要记住,仅仅因为模式被称为 “render prop” ,你不必使用名为 `render` 的 prop 来使用这种模式。 事实上, [组件能够知道什么需要渲染的*任何*函数 prop 在技术上都可以被称为 “render prop](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce).
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

Although the examples above use `render`, we could just as easily use the `children` prop!
尽管之前的例子使用了 `render`,我们也可以简单地使用 `children` prop

```js
<Mouse children={mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
<p> 鼠标的位置是{mouse.x}, {mouse.y}</p>
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved
LeoEatle marked this conversation as resolved.
Show resolved Hide resolved
)}/>
```

And remember, the `children` prop doesn't actually need to be named in the list of "attributes" in your JSX element. Instead, you can put it directly *inside* the element!
记住,`children` prop 并不真正需要添加到 JSX 元素的 “attributes” 列表中。相反,你可以直接放置到元素的*内部*!

```js
<Mouse>
{mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
<p>鼠标的位置是 {mouse.x}, {mouse.y}</p>
LeoEatle marked this conversation as resolved.
Show resolved Hide resolved
)}
</Mouse>
```

You'll see this technique used in the [react-motion](https://github.com/chenglou/react-motion) API.
您将在 [react-motion](https://github.com/chenglou/react-motion) API中看到此技术。
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

Since this technique is a little unusual, you'll probably want to explicitly state that `children` should be a function in your `propTypes` when designing an API like this.
由于这一技术的特殊性,当你在设计一个类似的 API 时,你可能要直接地在你的 propTypes 里声明 children 的类型应为一个函数。
lcxfs1991 marked this conversation as resolved.
Show resolved Hide resolved

```js
Mouse.propTypes = {
children: PropTypes.func.isRequired
};
```

## Caveats {#caveats}
## 注意事项 {#caveats}

### Be careful when using Render Props with React.PureComponent {#be-careful-when-using-render-props-with-reactpurecomponent}
### 将Render Props React.PureComponent一起使用时要小心 {#be-careful-when-using-render-props-with-reactpurecomponent}
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

Using a render prop can negate the advantage that comes from using [`React.PureComponent`](/docs/react-api.html#reactpurecomponent) if you create the function inside a `render` method. This is because the shallow prop comparison will always return `false` for new props, and each `render` in this case will generate a new value for the render prop.
如果你在 render 方法里创建函数,那么使用 render prop 会抵消使用 [`React.PureComponent`](/docs/react-api.html#reactpurecomponent) 带来的优势。因为浅比较 props 的时候总会得到 false ,并且在这种情况下每一个 `render` 对于 render prop 将会生成一个新的值。
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved

For example, continuing with our `<Mouse>` component from above, if `Mouse` were to extend `React.PureComponent` instead of `React.Component`, our example would look like this:
例如,继续我们之前使用的 `<Mouse>` 组件,如果 `Mouse` 继承自 `React.PureComponent` 而不是 `React.Component`,我们的例子看起来就像这样:

```js
class Mouse extends React.PureComponent {
// Same implementation as above...
// 与上面相同的代码......
}

class MouseTracker extends React.Component {
Expand All @@ -287,8 +286,8 @@ class MouseTracker extends React.Component {
<h1>Move the mouse around!</h1>

{/*
This is bad! The value of the `render` prop will
be different on each render.
这是不好的!
每个渲染的 `render` prop的值将会是不同的。
*/}
<Mouse render={mouse => (
<Cat mouse={mouse} />
Expand All @@ -299,14 +298,14 @@ class MouseTracker extends React.Component {
}
```

In this example, each time `<MouseTracker>` renders, it generates a new function as the value of the `<Mouse render>` prop, thus negating the effect of `<Mouse>` extending `React.PureComponent` in the first place!
在这样例子中,每次 `<MouseTracker>` 渲染,它会生成一个新的函数作为 `<Mouse render>` prop,因而在同时也抵消了继承自 `React.PureComponent` 的 `<Mouse>` 组件的效果!

To get around this problem, you can sometimes define the prop as an instance method, like so:
为了绕过这一问题,有时你可以定义一个 prop 作为实例方法,类似这样:

```js
class MouseTracker extends React.Component {
// Defined as an instance method, `this.renderTheCat` always
// refers to *same* function when we use it in render
// 定义为实例方法,`this.renderTheCat`始终
  // 当我们在渲染中使用它时,它指的是相同的函数
renderTheCat(mouse) {
return <Cat mouse={mouse} />;
}
Expand All @@ -322,4 +321,4 @@ class MouseTracker extends React.Component {
}
```

In cases where you cannot define the prop statically (e.g. because you need to close over the component's props and/or state) `<Mouse>` should extend `React.Component` instead.
如果您无法静态定义prop(例如,因为您需要关闭组件的 props 和/或 state),则 `<Mouse>` 应该扩展 `React.Component`
chenshuai2144 marked this conversation as resolved.
Show resolved Hide resolved