Skip to content

docs: translate Render Props #176

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

Merged
merged 3 commits into from
Sep 29, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 46 additions & 48 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)這個詞指的是一種用一個其值為函式的 prop 來在 React component 之間共享程式碼的技巧。

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 的 component 會接受一個函式,此函式不會實作他自己的 render 邏輯,而是會回傳並且呼叫一個 React element

```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/render-func), [Downshift](https://github.com/paypal/downshift) and [Formik](https://github.com/jaredpalmer/formik).
使用 render props 的函式庫包含 [React Router](https://reacttraining.com/react-router/web/api/Route/render-func)[Downshift](https://github.com/paypal/downshift) [Formik](https://github.com/jaredpalmer/formik)

In this document, we’ll discuss why render props are useful, and how to write your own.
以下我們會討論為什麼 render props 如此有用,以及怎麼實作自己的 render props。

## Use Render Props for Cross-Cutting Concerns {#use-render-props-for-cross-cutting-concerns}
## 為橫切關注點使用 Render Props{#use-render-props-for-cross-cutting-concerns}

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.
Component 是 React 中主要的程式碼重用單位,但如何將一個 component 所包含的 state 或行為共享給其他也同樣需要這些狀態或行為的 component 並不是那麼直觀。

For example, the following component tracks the mouse position in a web app:
例如,下面這個 component 負責在一個網頁應用中追蹤滑鼠游標的位置:

```js
class MouseTracker extends React.Component {
Expand Down Expand Up @@ -50,14 +50,14 @@ class MouseTracker extends React.Component {
}
```

As the cursor moves around the screen, the component displays its (x, y) coordinates in a `<p>`.
當游標在螢幕中移動時,這個 component 會在一個 `<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?
現在問題來了:我們該如何在其他 component 中重用這個行為呢?換句話說,如果另一個 component 需要知道游標的位置,我們能不能將這個行為封裝起來,讓其他的 component 能輕鬆地共享呢?

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.
既然 component 是 React 中程式碼基本重用單位,我們來試試看重構這段程式碼,改成用一個 `<Mouse>` component 來封裝這些需要在其他地方重用的行為。

```js
// The <Mouse> component encapsulates the behavior we need...
// <Mouse> component 封裝我們所需的行為...
class Mouse extends React.Component {
constructor(props) {
super(props);
Expand All @@ -76,7 +76,7 @@ class Mouse extends React.Component {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>

{/* ...but how do we render something other than a <p>? */}
{/* ...但我們如何 render 除了 <p> 以外的東西? */}
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
</div>
);
Expand All @@ -95,11 +95,11 @@ class MouseTracker extends React.Component {
}
```

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>` component 封裝了全部跟監聽 `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>` component ,它會在螢幕中 render 追著滑鼠跑的貓咪圖片。我們可能會用一個 `<Cat mouse={{ x, y }}>` prop 來告訴這個 component 滑鼠的座標,它便知道該把這張圖片放在螢幕中何處。

As a first pass, you might try rendering the `<Cat>` *inside `<Mouse>`'s `render` method*, like this:
作為第一次嘗試,你可能會試著把 `<Cat>` 從 *`<Mouse>` `render` 方法裡面* render 出來,像這樣:

```js
class Cat extends React.Component {
Expand Down Expand Up @@ -130,10 +130,9 @@ class MouseWithCat extends React.Component {
<div style={{ height: '100vh' }} 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> ...但這樣我們就必須在每次用到它時,
創建另外一個 <MouseWithSomethingElse> component,
所以 <MouseWithCat> 的可重用性還不夠。
*/}
<Cat mouse={this.state} />
</div>
Expand All @@ -153,9 +152,9 @@ class MouseTracker extends React.Component {
}
```

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.
此方法在特定案例中可行,但我們還未能以可重用的方法把這個行為封裝起來。現在,每次在不同案例中,如果我們需要知道滑鼠位置,便必須另外創建一個特定為該案例 render 某些東西的 component (換句話說,就是另一個 `<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 發揮功用的地方了:我們提供 `<Mouse>` 一個 function prop --- render prop,讓它能夠動態決定該 render 什麼,而不是把 `<Cat>` 寫死在 `<Mouse>` component 裡。

```js
class Cat extends React.Component {
Expand Down Expand Up @@ -186,8 +185,7 @@ class Mouse extends React.Component {
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>

{/*
Instead of providing a static representation of what <Mouse> renders,
use the `render` prop to dynamically determine what to render.
用 `render` prop 去動態決定該 render 什麼,而不是將 <Mouse> render 的東西靜態表示出來。
*/}
{this.props.render(this.state)}
</div>
Expand All @@ -209,17 +207,18 @@ 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>` ,讓它能夠動態決定它該 render 什麼,而不是為了一些特定案例,去複製 `<Mouse>` component 並在它的 `render` 方法中硬性寫入某些東西。

More concretely, **a render prop is a function prop that a component uses to know what to render.**
更具體地說,**render prop 是一個讓 component 知道該 render 什麼的 function prop。**

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.

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 一個有 `render` prop 的 `<Mouse>`,讓 `render` prop 來告訴 `<Mouse>` 該用現在游標的 (x,y) render 什麼。

關於 render props 一件有趣的事是,你可以用包含 render prop 的普通 component 來實作最 [higher-order component](/docs/higher-order-components.html)。舉例來說,如果你偏好用 `withMouse` HOC 而不是 `<Mouse>` component 的話,你可以輕易地用一個普通的 `<Mouse>` 加上 render prop 來建立:

```js
// If you really want a HOC for some reason, you can easily
// create one using a regular component with a render prop!
// 如果你真的想用 HOC ,你可以輕易地用一個
// 有 render prop 的普通 component 來建立!
function withMouse(Component) {
return class extends React.Component {
render() {
Expand All @@ -233,21 +232,21 @@ 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}

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 props」,不代表*你一定要用一個名為 `render` 的 prop來使用這個模式*。事實上,[*任何*是函式且被 component 用來認知該 render 什麼的 prop,都叫做 「redner prop](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce)

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>
)}/>
```

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 element 的屬性列表中註明。你可以直接把它放在 element *裡面*!

```js
<Mouse>
Expand All @@ -257,27 +256,27 @@ And remember, the `children` prop doesn't actually need to be named in the list
</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 中被使用。

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` 定義為一個函式。

```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}

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 而言,prop 的淺比較會永遠回傳 `false`,並且每次 `render` 都會為 render prop 產生新的值。

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>` component,如果 `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,7 @@ 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 的值在每次 render 都會不一樣。
*/}
<Mouse render={mouse => (
<Cat mouse={mouse} />
Expand All @@ -299,14 +297,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>` render 時,它會產生一個新的函式,作為 `<Mouse render>` prop 的值,便使一開始 `<Mouse>` 繼承 `React.PureComponent` 的效果作廢!

To get around this problem, you can sometimes define the prop as an instance method, like so:
為了避開這個問題,你有時候可以把這個 prop 定義為一個 instance 方法,像是:

```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` 被定義成一個 instance 方法
// 當我們在 render 使用到時,會永遠指向*相同的*函式
renderTheCat(mouse) {
return <Cat mouse={mouse} />;
}
Expand All @@ -322,4 +320,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 定義成靜態的案例中(例:因為你需要封閉 componentprops 和/或 state),`<Mouse>` 應改為繼承 `React.Component`