I've been working through the examples in relay's document for QueryRenderer https://relay.dev/docs/en/query-renderer and FragmentContainer https://relay.dev/docs/en/fragment-container
I'm confused as to how the data is meant to be accessed by the Components wrapped in the HOC createFragmentContainer.
I have a top level 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 }
/>
);
}
and a TodoList component that defines what data is needed in a graphql fragment:
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
}
}
`,
})
and a child 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
}
`
});
My understanding is that the createFragmentContainer for the TodoList will inject the data from the TodoList_tdlist fragemnt as the TodoList props.tdlist
), shaped as per the shape of the graphql query.
However, this appears to not be happening. I get a warning in the console:
Warning: createFragmentSpecResolver: Expected prop `tdlist` to be supplied to `Relay(TodoList)`, but got `undefined`. Pass an explicit `null` if this is intentional.
What is the job of the createFragmentContainer if it is not to pass in the tdlist?
I tried to pass the todoList data explicitly in by changing return to return (the same prop name passed in by createFragmentContainer, I (understandably) get a different error:
Warning: RelayModernSelector: Expected object to contain data for fragment `TodoList_tdlist`, got `{"title":"Your to-do list","todoListItems":[{"text":"Brush teeth","isComplete":false},....
I think the root of my confusion is not understanding how the fragments that define the data dependencies interact with the QueryRenderer. Do I need to define the query to pull in every possible piece of data that could ever be needed, and the point is that relay will only query what is needed by looking at the graphql fragments of the components that are being rendered now, and will re-query if that changes, updating props as it gets new data?
Do I need to pass props down to fragment containers as props explicitly, or if they are a descendant of a QueryRenderer that requests their data, will the createFragmentContainer be able to access it via the relay environment?
Here is my graphql.schema to assist:
type Query {
todoList: TodoList!
}
type Todo {
text: String!
isComplete: Boolean!
}
type TodoList {
title: String!
todoListItems: [Todo]!
}