My goal is to combine multiple nested sibling arrays
of edges
from the same Relay fragment
into one render
call.
Backend data is delivered via Relay
containers (also using react-router-relay
).
I have a component hierarchy that displays multiple Tags
from several different Users
. The top-level query looks something like this:
getUserGroup(id: 'abc') {
users {
edges { // array of arrays objects with nested arrays
node {
Tags {
edges { // array of objects with target items
node {
tagName // target item
id
}
}
}
}
}
}
}
Which results in something like this (in fully-flattened form):
results = [['tagname1', 'tagname2'], [tagname3, tagname4]]
Currently I render a hierarchy with each node Type
in its own Component
, i.e., Tagmenu
-> UserTagGroup
-> TagItems
(code is at the bottom of this post).
This groups all of the tags by User
, eventually rendering a list like this:
User 1:
- Tag 1
- Tag 2
User 2:
- Tag 3
- Tag 4
What I'd like to achieve is a render
where all Users
' tags are mixed-in together at the second tier, i.e., a hierarchy like TagMenu
-> TagItems
, to render:
User Tags
- Tag 1
...
- Tag 4
The only way I can manage so far is to manually extract and combine all of arrays from the top-level Relay
container results with something like this (pseudo code):
for each array in users.edges:
for each array in node.Tags.edges:
return node.tagName
This doesn't seem right for 2 reasons:
- It's a bit much to pack into a
render()
function, - It's not clear if it's possible to protect against
null
refs withdefault props
in Relay
... but it's obviously doable.
My question is: what's the Relay
way to do this? Given how naturally the library leads to component composition, I can't imagine that pulling deeply nested results at a higher level and manually shuffling them is optimal. Here are my components:
// TagsMenu component, top level
class TagsMenu extends Component {
render() {
return (
<div>
{
this.props.userGroup.users.edges.map(u => {
return <UserTagGroup user={u.node} />
})
}
</div>
)
}
}
fragment on UserGroup {
users(first: 1000) {
edges {
node {
${UserTagGroup.getFragment('user')}
}
}
}
}
// UserTagGroup, second component
class UserTagGroup extends Component {
render() {
return (
<div>
<h4>User: {this.props.user.id}</h4>
{
this.props.user.Tags.edges.map(t => {
return <TagMenuItem tag={t.node} />
})
}
</div>
)
}
}
fragment on User {
id
listingTags(first: 1000) {
edges {
node {
${TagMenuItem.getFragment('tag')}
}
}
}
}
// TagMenuItem, bottom level component. Renders 1 Tag.
class TagMenuItem extends Component {
render() {
return (
<div>
{this.props.tag.tagName}
</div>
)
}
}
fragment on Tag {
tagName
id
}
What you've been describing - "manually extract and combine all of arrays from the top-level Relay container" - do seem like the way to go, though.
If the problem lies in having this function in the
render()
method, I suggest you to use a combination of state and the methodcomponentWillReceiveProps()
. The goal being to recompute the flattened list only whenthis.props.users
has truly changed.Something along theses line :