React Relay createFragmentContainer 和 QueryRenderer 数据流
React Relay createFragmentContainer and QueryRenderer data flow
我一直在研究 QueryRenderer 的中继文档中的示例 https://relay.dev/docs/en/query-renderer and FragmentContainer https://relay.dev/docs/en/fragment-container
我对 HOC createFragmentContainer 中包装的组件如何访问数据感到困惑。
我有一个顶级 QueryRenderer:
function renderFunction({error, props}) {
...
if (props) {
// Added todoList as a prop with a distinct name from tdlist which
// is meant to be passed in by createFragmentContainer
return <TodoList todoList = {props.todoList} />
}
...
}
export default function ContainerWithQueryRenderer() {
return (
<QueryRenderer
environment={environment}
query={graphql`
query ContainerWithQueryRenderer_Query {
todoList {
title
todoListItems { text isComplete}
}
}`}
render = { renderFunction }
/>
);
}
以及定义 graphql 片段中需要哪些数据的 TodoList 组件:
import React from 'react';
import TodoItem from './TodoItem.js';
import { createFragmentContainer } from 'react-relay';
import graphql from 'babel-plugin-relay/macro';
function TodoList(props) {
console.log('TodoList props:',props);
if (props && props.todoList && props.todoList.todoListItems && props.todoList.title) {
return (
<div className='list-container'>
<div className='list-header'>
<div className='list-header-label'>{props.todoList.title}</div>
</div>
<div className='list'>{props.todoList.todoListItems.map( (item, key) => <TodoItem key = {key} item={item} />)}</div>
</div>
);
} else {
return null;
}
}
export default createFragmentContainer(TodoList,{
tdlist: graphql`
fragment TodoList_tdlist on TodoList {
title
todoListItems {
...TodoItem_item
}
}
`,
})
和一个子 TodoListItem
import React from 'react';
import { createFragmentContainer } from 'react-relay';
import graphql from 'babel-plugin-relay/macro';
function TodoItem(props) {
return <div className='list-item'>
<div className='list-item-label'>{props.item.text}</div>
{props.item.isComplete ?<div className='list-item-buttons'>Done</div>:null}
</div>
}
export default createFragmentContainer( TodoItem,{
item: graphql`
fragment TodoItem_item on Todo {
text isComplete
}
`
});
我的理解是 TodoList 的 createFragmentContainer 将从 TodoList_tdlist fragemnt 中注入数据作为 TodoList props.tdlist
),形状根据 graphql 查询的形状。
然而,这似乎并没有发生。我在控制台中收到警告:
Warning: createFragmentSpecResolver: Expected prop `tdlist` to be supplied to `Relay(TodoList)`, but got `undefined`. Pass an explicit `null` if this is intentional.
createFragmentContainer如果不是传入tdlist有什么作用?
我试图通过更改显式传递 todoList 数据
return
至
return
(通过 createFragmentContainer 传入相同的 prop 名称,我(可以理解)得到不同的错误:
Warning: RelayModernSelector: Expected object to contain data for fragment `TodoList_tdlist`, got `{"title":"Your to-do list","todoListItems":[{"text":"Brush teeth","isComplete":false},....
我认为我困惑的根源在于不了解定义数据依赖项的片段如何与 QueryRenderer 交互。我是否需要定义查询以提取可能需要的所有可能的数据,关键是中继将仅通过查看现在正在呈现的组件的 graphql 片段来查询所需的内容,并且将重新查询是否发生变化,在获取新数据时更新道具?
我是否需要将道具作为道具显式传递给片段容器,或者如果它们是请求其数据的 QueryRenderer 的后代,createFragmentContainer 是否能够通过中继环境访问它?
这是我的 graphql.schema 协助:
type Query {
todoList: TodoList!
}
type Todo {
text: String!
isComplete: Boolean!
}
type TodoList {
title: String!
todoListItems: [Todo]!
}
您必须在 QueryRenderer 中命名后代片段容器的 GraphQL 片段。在不识别片段的情况下,react-relay 在 QueryRenderer 和后代 FragmentContainer 组件之间没有中继 link。在您的问题中,传递给 QueryRenderer 的 query
道具是
query ContainerWithQueryRenderer_Query { todoList { title todoListItems { text isComplete }}}
而不是
query ContainerWithQueryRenderer_Query { TodoList_tdlist }
此外,由于 QueryRenderer 与片段容器协同工作,因此在查询渲染器中迭代 todoListItems 并为 TodoItem_item 使用片段会更简单。因此在下面我合并了上面的 <ContainerWithQueryRenderer />
和 <TodoList />
这种方法有效:
TodoListWithQueryRenderer.js:
function renderFunction({error, props}) {
...
if (props) {
return (
<div>
<div>
<div>{props.todoList.title}</div>
</div>
<div>{props.todoList.todoListItems.map( (item, key) => <TodoItem key = {key} item={item} /> )}</div>
</div>
);
}
...
}
export default function TodoListWithQueryRenderer() {
return (
<QueryRenderer
environment={environment}
query={graphql`
query TodoListWithQueryRenderer_Query { todoList {todoListItems {...TodoItem_item } title} }
`}
render = { renderFunction }
/>
);
}
只需要一个后代组件,因为上面将 ContainterWithQueryRenderer 与 TodoList 组件合并。
TodoItem.js:
import React from 'react';
import { createFragmentContainer } from 'react-relay';
import graphql from 'babel-plugin-relay/macro';
function TodoItem(props) {
return <div>
<div>{props.item.text}</div>
{props.item.isComplete ? <div>Done</div> : null}
</div>
}
export default createFragmentContainer( TodoItem,{
item: graphql`
fragment TodoItem_item on Todo {
text isComplete
}
`
});
我一直在研究 QueryRenderer 的中继文档中的示例 https://relay.dev/docs/en/query-renderer and FragmentContainer https://relay.dev/docs/en/fragment-container
我对 HOC createFragmentContainer 中包装的组件如何访问数据感到困惑。
我有一个顶级 QueryRenderer:
function renderFunction({error, props}) {
...
if (props) {
// Added todoList as a prop with a distinct name from tdlist which
// is meant to be passed in by createFragmentContainer
return <TodoList todoList = {props.todoList} />
}
...
}
export default function ContainerWithQueryRenderer() {
return (
<QueryRenderer
environment={environment}
query={graphql`
query ContainerWithQueryRenderer_Query {
todoList {
title
todoListItems { text isComplete}
}
}`}
render = { renderFunction }
/>
);
}
以及定义 graphql 片段中需要哪些数据的 TodoList 组件:
import React from 'react';
import TodoItem from './TodoItem.js';
import { createFragmentContainer } from 'react-relay';
import graphql from 'babel-plugin-relay/macro';
function TodoList(props) {
console.log('TodoList props:',props);
if (props && props.todoList && props.todoList.todoListItems && props.todoList.title) {
return (
<div className='list-container'>
<div className='list-header'>
<div className='list-header-label'>{props.todoList.title}</div>
</div>
<div className='list'>{props.todoList.todoListItems.map( (item, key) => <TodoItem key = {key} item={item} />)}</div>
</div>
);
} else {
return null;
}
}
export default createFragmentContainer(TodoList,{
tdlist: graphql`
fragment TodoList_tdlist on TodoList {
title
todoListItems {
...TodoItem_item
}
}
`,
})
和一个子 TodoListItem
import React from 'react';
import { createFragmentContainer } from 'react-relay';
import graphql from 'babel-plugin-relay/macro';
function TodoItem(props) {
return <div className='list-item'>
<div className='list-item-label'>{props.item.text}</div>
{props.item.isComplete ?<div className='list-item-buttons'>Done</div>:null}
</div>
}
export default createFragmentContainer( TodoItem,{
item: graphql`
fragment TodoItem_item on Todo {
text isComplete
}
`
});
我的理解是 TodoList 的 createFragmentContainer 将从 TodoList_tdlist fragemnt 中注入数据作为 TodoList props.tdlist
),形状根据 graphql 查询的形状。
然而,这似乎并没有发生。我在控制台中收到警告:
Warning: createFragmentSpecResolver: Expected prop `tdlist` to be supplied to `Relay(TodoList)`, but got `undefined`. Pass an explicit `null` if this is intentional.
createFragmentContainer如果不是传入tdlist有什么作用?
我试图通过更改显式传递 todoList 数据 return 至 return (通过 createFragmentContainer 传入相同的 prop 名称,我(可以理解)得到不同的错误:
Warning: RelayModernSelector: Expected object to contain data for fragment `TodoList_tdlist`, got `{"title":"Your to-do list","todoListItems":[{"text":"Brush teeth","isComplete":false},....
我认为我困惑的根源在于不了解定义数据依赖项的片段如何与 QueryRenderer 交互。我是否需要定义查询以提取可能需要的所有可能的数据,关键是中继将仅通过查看现在正在呈现的组件的 graphql 片段来查询所需的内容,并且将重新查询是否发生变化,在获取新数据时更新道具?
我是否需要将道具作为道具显式传递给片段容器,或者如果它们是请求其数据的 QueryRenderer 的后代,createFragmentContainer 是否能够通过中继环境访问它?
这是我的 graphql.schema 协助:
type Query {
todoList: TodoList!
}
type Todo {
text: String!
isComplete: Boolean!
}
type TodoList {
title: String!
todoListItems: [Todo]!
}
您必须在 QueryRenderer 中命名后代片段容器的 GraphQL 片段。在不识别片段的情况下,react-relay 在 QueryRenderer 和后代 FragmentContainer 组件之间没有中继 link。在您的问题中,传递给 QueryRenderer 的 query
道具是
query ContainerWithQueryRenderer_Query { todoList { title todoListItems { text isComplete }}}
而不是
query ContainerWithQueryRenderer_Query { TodoList_tdlist }
此外,由于 QueryRenderer 与片段容器协同工作,因此在查询渲染器中迭代 todoListItems 并为 TodoItem_item 使用片段会更简单。因此在下面我合并了上面的 <ContainerWithQueryRenderer />
和 <TodoList />
这种方法有效:
TodoListWithQueryRenderer.js:
function renderFunction({error, props}) {
...
if (props) {
return (
<div>
<div>
<div>{props.todoList.title}</div>
</div>
<div>{props.todoList.todoListItems.map( (item, key) => <TodoItem key = {key} item={item} /> )}</div>
</div>
);
}
...
}
export default function TodoListWithQueryRenderer() {
return (
<QueryRenderer
environment={environment}
query={graphql`
query TodoListWithQueryRenderer_Query { todoList {todoListItems {...TodoItem_item } title} }
`}
render = { renderFunction }
/>
);
}
只需要一个后代组件,因为上面将 ContainterWithQueryRenderer 与 TodoList 组件合并。
TodoItem.js:
import React from 'react';
import { createFragmentContainer } from 'react-relay';
import graphql from 'babel-plugin-relay/macro';
function TodoItem(props) {
return <div>
<div>{props.item.text}</div>
{props.item.isComplete ? <div>Done</div> : null}
</div>
}
export default createFragmentContainer( TodoItem,{
item: graphql`
fragment TodoItem_item on Todo {
text isComplete
}
`
});