原文地址:Containers
上一篇 Tutorial
Relay文档翻译目录
The primary way to declare data requirements is via Relay.Container
— a higher-order React component that lets React components encode their data requirements.
在Relay中主要通过Relay.Container
来声明数据需求,它是高阶React component,用于让React component描述自己的数据需求。所谓高阶,如同高阶函数可以接收一个函数作为参数,同样的它可以接收一个React component作为参数。
Similar to how a React component’s render
method does not directly modify native views, Relay containers do not directly fetch data. Instead, containers declare a specification of the data needed to render. Relay guarantees that this data is available before rendering.
我们都知道React component的render方法并没有直接修改真实的dom节点,而是通过virtual dom等技术实现。与之类似,Relay containers并不是直接获取数据。取而代之的是,containers声明需要render的数据说明。Relay保证在数据被渲染以前是可用的。
To start, let’s build the plain React version of a
component that displays the user’s profile photo and a slider to adjust the photo’s size.
一开始,我们先构建一个纯React版的
component,用于显示用户简介中的照片,还有一个滑动条可以调整照片大小。
Here’s a basic implementation of
that ignores styling in order to highlight the functionality:
下面是
的基本实现,为突出功能忽略了css style
class ProfilePicture extends React.Component {
render() {
// Expects the `user` prop to have the following shape:
// {
// profilePhoto: {
// uri,
// size
// }
// }
var user = this.props.user;
return (
this.setSize(value)} />
);
}
// Update the size of the photo
setSize(photoSize) {
// TODO: Fetch the profile photo URI for the given size...
}
}
In Relay, data dependecies are described using GraphQL. For
, the dependency can be expressed as follows. Note that this exactly matches the shape that the component expected for the user
prop.
在Relay中使用 GraphQL描述数据依赖关系。
的依赖关系见下面代码。它精确的匹配了component上的数据需求,此例映射到user prop上。
Relay.QL`
# This fragment only applies to objects of type `User`.
fragment on User {
# Set the `size` argument to a GraphQL variable named `$size` so that we can
# later change its value via the slider.
profilePhoto(size: $size) {
# Get the apppropriate URI for the given size, for example on a CDN.
uri,
},
}
`
注释:译者在下面代码中进行了解读,帮助理解
Given the plain React component and a GraphQL fragment, we can now define a Container
to tell Relay about this component’s data requirements. Let’s look at the code first and then see what’s happening:
给出了纯的React component和一个GraphQL fragment。我们现在可以定义一个Container
来告诉Relay那个纯的React component的数据需求。
class ProfilePicture extends React.Component {/* as above */}
// Export a *new* React component that wraps the original ``.
// Relay.createContianer接收一个ProfilePicture component,返回一个新的React component,并且将其输出。
module.exports = Relay.createContainer(ProfilePicture, {
// Specify the initial value of the `$size` variable.
// initialVariables中可以初始化一些变量
initialVariables: {
size: 32
},
// For each of the props that depend on server data, we define a corresponding
// key in `fragments`. Here, the component expects server data to populate the
// `user` prop, so we'll specify the fragment from above as `fragments.user`.
// 对于每一个依赖服务器端数据的prop,我们在fragments中定义一个相应的key,
// 这里component期望服务器端获取数据放入user prop,所以我们把之前上面那段代码,放入到fragments.user中。
// fragments对象中的每个属性,这里如user,都可以在与之绑定的React Component(这里是ProfilePicture)中的props中获取,这里是this.props.user
fragments: {
user: () => Relay.QL`
fragment on User {
#在服务器端schema中一定有对应的User
profilePhoto(size: $size) {
#在schema User下一定有方法profilePhoto
uri,
#该方法一定有对应的返回值
},
}
`,
},
});
为了便于理解,可以对照schema一起看
Relay containers are higher-order components — Relay.createContainer
is a function that takes a React component as input and returns a new component as output. This means that the container can manage data fetching and resolution logic without interfering with the state
of the inner component.
Relay container 是高阶componet。Relay.createContainer
是一个函数可以接收React component作为输入,返回一个新的component作为输出。这意味着container可以管理数据获取和解析逻辑,并且不需要使用组件内部的state
。(注:这是一个巨大的好处)
Here’s what happens when the container is rendered:
这里展示了当container进行render的时候发生了什么:
In the diagram above:
User
“record”.Relay(ProfilePicture)
for debugging — will retrieve the response for each GraphQL fragment from the local store.
component.
receives a user
prop with plain JavaScript data - objects, arrays, strings - and renders as usual.Relay(ProfilePicture)
这个container将通过每一个GraphQL fragment从本地的store中获取返回值
component
接收到一个user prop,其中包含着普通的JS数据,如object,arrays, string,之后像原来一样renderOne thing is left in the example above — implementing setSize()
, which should change the photo’s size when the slider values changes. In addition to passing the results of each query to the component, Relay also provides a relay
prop that has Relay-specific methods and metadata. These include variables
— the active variables used to fetch the current props
— and setVariables()
— a callback that can be used to request data for different variable values.
上面例子中还有一件事要做,实现setSize()方法,当slider变动时可以改变照片size。除了可以传递query结果给component,Relay还提供了’relay’ prop,其中还有Relay定制的方法与元数据。variables
就是其中一个,用于获取当前props,setVariables()
是一个回调用于为不同的变量值请求数据。
class ProfilePicture extends React.Component {
render() {
// Access the resolved data for the `user` fragment.
// 访问user fragment的返回数据
var user = this.props.user;
// Access the current `variables` that were used to fetch the `user`.
var variables = this.props.relay.variables;
return (
this.setSize(value)} />
);
}
// Update the size of the photo.
setSize(photoSize) {
// `setVariables()` tells Relay that the component's data requirements have
// changed. The value of `props.relay.variables` and `props.user` will
// continue to reflect their previous values until the data for the new
// variables has been fetched from the server. As soon as data for the new
// variables becomes available, the component will re-render with an updated
// `user` prop and `variables.size`.
this.props.relay.setVariables({
size: photoSize,
});
}
}
React and Relay support creating arbitrarily complex applications through composition. Larger components can be created by composing smaller components, helping us to create modular, robust applications. There are two aspects to composing components in Relay:
React和Relay支持通过composition的方式创建任意复杂的应用。大一些的component可以由小的component构成,这有助于建立模块化的、健壮的应用。在Relay中构建component包括以下两个方面:
Let’s explore how this works via a
component that composes the
from above.
我们通过看
如何使用之前
进行构建做进一步了解。
View composition is exactly what you’re used to — Relay containers are standard React components. Here’s the
component:
视图构建和你已经习惯的react中的工作方式一模一样,Relay container就是标准的React component。以下是
component
class Profile extends React.Component {
render() {
// Expects a `user` with a string `name`, as well as the information
// for `` (we'll get that next).
var user = this.props.user;
return (
{/* It works just like a React component, because it is one! */}
{user.name}
);
}
}
Fragment composition works similarly — a parent container’s fragment composes the fragment for each of its children. In this case,
needs to fetch information about the User
that is required by
.
Fragment composition工作方式类似,父container的fragment由包含的子fragment构成。在这个例子中
获取被
需要的User数据。
Relay containers provide a static getFragment()
method that returns a reference to that component’s fragment:
Relay containers 提供了一个静态方法getFragment()
参数接收一个子fragment的属性,返回该子fragment的引用。
class Profile extends React.Component {/* as above */}
module.exports = Relay.createContainer(Profile, {
fragments: {
// This `user` fragment name corresponds to the prop named `user` that is
// expected to be populated with server data by the `` component.
user: () => Relay.QL`
fragment on User {
# Specify any fields required by `` itself.
name,
# Include a reference to the fragment from the child component. Here,
# the `user` is the name of the fragment specified on the child
# ``'s `fragments` definition.
${ProfilePicture.getFragment('user')},
}
`,
}
});
The final data declaration is equivalent to the following plain GraphQL:
上面最后那段数据声明等同于如下普通GraphQL
`
fragment Profile on User {
name,
...ProfilePhoto,
}
fragment ProfilePhoto on User {
profilePhoto(size: $size) {
uri,
},
}
`
Note that when composing fragments, the type of the composed fragment must match the field on the parent in which it is embedded. For example, it wouldn’t make sense to embed a fragment of type Story
into a parent’s field of type User
. Relay and GraphQL will provide helpful error messages if you get this wrong (and if they aren’t helpful, let us know!).
注意当构建fragment的时候,被用来构建的fragment type必须与其父上的field匹配。例如,把Story
type 对应到其父的User type field。Relay和GraphQL将提供辅助的错误提示。
As we’ve learned, Relay containers declare data requirements as GraphQL fragments. This means that, for example,
can be embedded not only in
, but any container that fetches a field of type User
.
正如我们所学,Relay container通过GraphQL fragment来声明数据需求。这意味着,如
不仅可以被嵌在
中,而是任何使用User type field的container都可以。
We’re almost ready to let Relay fulfill the data requirements for these components and render them. However, there is one problem. In order to actually fetch data with GraphQL, we need a query root. For example, we need to ground the
fragment in a concrete node of type User
.
我们几乎快要全部满足这些组件的数据需求并可以渲染他们了。但是还有一个问题。为了切实的通过GraphQL获取数据,我们还需要一个query root。例如,我们需要把
对应到具体的User
type node上。
In Relay, the root of a query is defined by a Route. Continue to learn about Relay routes.
在Relay中,使用Route定义查询的根,我们随后继续学习。