Skip to content
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

recursive data #246

Closed
Gregoor opened this issue Sep 5, 2015 · 14 comments
Closed

recursive data #246

Gregoor opened this issue Sep 5, 2015 · 14 comments

Comments

@Gregoor
Copy link
Contributor

Gregoor commented Sep 5, 2015

First off all, love idea and the great talks about it on ReactEurope. But I still get stuck quite a lot (which I should expect from a technical preview). Here is my current problem:

My data is modeled like this:

type Babushka {
  id: !String,
  name: !String,
  babushkas: [Babushka]
}

Now I want to display a Babushka list for which I have a List and a ListItem Component:

list.js

class BabushkaList extends Component {

  render() {
    return <ul>{this.props.babushka.babushkas.map(b => <BabushkaListItem babushka={b}/>)}</ul>;
  }

}

export default Relay.createContainer(BabushkaList, {

  fragments: {
    babushka: () =>  Relay.QL`
      fragment on Babushka {
        babushka {
          ${BabushkaListItem.getFragment('babushka')}
        }
      }
    `
  }

});

list-item.js

class BabushkaListItem extends Component {


  render() {
    return (
      <li onClick={this.onClick.bind(this)}>
        {this.props.babushka.name}
        <ul><BabushkatList babushka={this.props.babushka}</ul>
      </li>
    );
  }

}

export default Relay.createContainer(BabushkaListItem, {

  fragments: {
    babushka: () =>  Relay.QL`
      fragment on Babushka{
        name,
        ${BabushkaList.getFragment('babushka')}
      }
    `
  }

});

Naturally this leads to the stack size being exceeded. Am I doing something wrong fragment wise?

@josephsavona
Copy link
Contributor

Recursive fragments are supported so long as you explicitly specify a stopping point. The pattern for this is to create a variable that determines if the nested item should be expanded or not - default it to false, and then only "expand" the items one level deep to begin with:

export default Relay.createContainer(BabushkaListItem, {
  initialVariables: {
    expand: false, // use `this.props.relay.setVariables({expand: true})` to expand an additional level
  },
  fragments: {
    babushka: (variables) =>  Relay.QL`  # note the `variables` argument here
      fragment on Babushka{
        name,
        ${BabushkaList.getFragment('babushka').if(variables.expand)} # getFragment().if(variables.foo)
      }
    `
  }
});

The parent component of this list can then specify that the first level of items should be expanded initially:

Relay.createContainer(Foo, {
  fragments: {
    foo: () => Relay.QL`
      fragment on Foo {
        ${BabushkaList.getFragment('babushka', {expand: true})} #override the default value to expand a single level of the list
      ...

@Gregoor
Copy link
Contributor Author

Gregoor commented Sep 6, 2015

Just what I was looking for! Thanks a lot Joesph.

@Gregoor
Copy link
Contributor Author

Gregoor commented Sep 6, 2015

Now I'm getting the following error:

Warning: RelayContainer: Expected prop `babushka` supplied to `BabushkaList` to be data fetched
by Relay. This is likely an error unless you are purposely passing in mock data that conforms to
the shape of this component's fragment.

Which doesn't make a whole lot of sense to me, because the babushka being passed to the List component is obviously fetched by Relay and not mocked by me. Am I missing something?

@josephsavona
Copy link
Contributor

@Gregoor is it possible to link to a gist of the code for the parent and child component?

@Gregoor
Copy link
Contributor Author

Gregoor commented Sep 6, 2015

Does this suffice?

I can't really make sense of the error in that context. :( Thanks for getting back to me so quickly

@josephsavona
Copy link
Contributor

Commented on the gist, but since SpeciesList expects the species prop to be an array of species, it should mark the fragment as plural: fragment on Species @relay(plural:true) {...}.

@Gregoor
Copy link
Contributor Author

Gregoor commented Sep 7, 2015

Out of all the words in the english language, I pick one where the singular and plural is isomorphic. Oh brain, why?

Anyway thanks, didn't know about the annotations(?). Are they already documented somewhere?
But I do think that species is singular in both cases, since the list component fetches the subspecies itself (which should be available).

I changed the naming once again to something more familiar and heteromorphic.

@Gregoor
Copy link
Contributor Author

Gregoor commented Sep 10, 2015

@grifx
Copy link

grifx commented Oct 1, 2015

Hi @Gregoor @josephsavona ...,
I'm a little bit confused. React, Relay and GraphQL are quite new for me.
Is it possible for example to render recursively a directory structure with n descendants in a single http request?
If not, what's the source of the "problem"?

  • GraphQL doesn't provide a syntax to query recursively data
  • or Relay doesn't know how to use this GraphQL syntax.

Relay batch the queries so in the worst case scenario, if I understand, I'll have a single GraphQL Query per level and by using a condition like ${BabushkaList.getFragment('babushka') we can tweak the thing to get n levels at the same time.

Thanks for clarifications.

@kassens
Copy link
Member

kassens commented Oct 1, 2015

@grifx: Instead of a boolean you can use a number that you decrement every level of recursion. If it's <= 0 you don't expand.

@dminkovsky
Copy link
Contributor

What is the deal with #if() in BabushkaList.getFragment('babushka').if(variables.expand)? It looks like RelayContainer.getFragment() returns a RelayQueryFragment which doesn't seem to have an #if() on its prototype.

** Edit **

Posted too soon: it's a RelayFragmentReference. Will continue investigating but any guidance regarding where I might read (non-code) about this would be great.

@josephsavona
Copy link
Contributor

@dminkovsky RelayFragmentReference and the if/unless functions on it aren't documented. The plan is to replace these with standard GraphQL @include/@skip directives so that you could write something like:

fragment on Foo {
  ${Child.getFragment('foo')} @include(if: $var) @relay(variables: {foo: $bar})
}

@dminkovsky
Copy link
Contributor

@joesavona I was wondering just that—the relationship between if/unless and
directives. Thanks for clarifying!

понедельник, 4 января 2016 г. пользователь Joseph Savona написал:

@dminkovsky https://github.com/dminkovsky RelayFragmentReference and
the if/unless functions on it aren't documented. The plan is to replace
these with standard GraphQL @include/@Skip directives.


Reply to this email directly or view it on GitHub
#246 (comment).

@MartinDawson
Copy link

@josephsavona Hey, I tried converting your example for specifying 1 level deep for relay-modern but I still get the error:

comments/commentContainer.js: Cannot spread fragment "commentContainer_comment" within itself via repliesContainer.

CommentContainer.js:

const fragments = graphql`
  fragment commentContainer_comment on Comment {
    commentId
    body
    dateAdded
    likes
    dislikes
    originalComment {
      commentId
    }
    user {
      userName
    }
    ...repliesContainer
  }
`;

RepliesContainer.js:

const fragments = graphql`
  fragment repliesContainer on Comment
  @argumentDefinitions(
    count: { type: "Int", defaultValue: 3 }
    expandReplies: { type: "Boolean"}
  ) {
    id
    replies(
      first: $count
      after: $cursor
    ) {
      totalCount
      items {
        commentId
        ...commentContainer_comment @include (if: $expandReplies)
      }
    }
  }
`;

And then in my root query I pass down expandReplies: true.

Do you know what I am doing wrong? I only want to include commentContainer_comment once and not infinitely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants