Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive data & components, later fetches throwing an error

First off my graphql data model:

type Human {
  id: !String,
  name: !String,
  children: [Human]
}

The only route (relay route config) I'm atm using:

class extends Relay.Route {
  static queries = {
    human: () => Relay.QL`query RootQuery { viewer }`
  };
  static routeName = 'AppHomeRoute';
}

The list component:

class HumanList extends Component {
  render() {
    let {children} = this.props.human;

    let subListsHTML = human ? children.map(child => (
      <HumanListItem key={child.id} human={child}/>
    )) : '';

    return <ul>{subListsHTML}</ul>;
  }
}

export default Relay.createContainer(HumanList, {
  fragments: {
    human: () =>  Relay.QL`
      fragment on Human {
        children {
          id,
          ${HumanListItem.getFragment('human')}
        }
      }
    `
  }
});

The list item component:

class HumanListItem extends Component {
  state = {expanded: false};

  render() {
    let {human} = this.props;

    let sublistHTML = '';
    if (this.state.expanded) {
      sublistHTML = <ul><HumanList human={human}/></ul>;
    }

    return (
      <li>
        <div onClick={this.onClickHead.bind(this)}>{human.name}</div>
        {sublistHTML}
      </li>
    );
  }

  onClickHead() {
    this.props.relay.setVariables({expanded: true});
    this.setState({expanded: true});
  }

}

HumanListItem.defaultProps = {viewer: {}};

export default Relay.createContainer(HumanListItem, {

  initialVariables: {
    expanded: false
  },

  fragments: {
    human: (variables) =>  Relay.QL`
      fragment on Human {
        name,
        ${HumanList.getFragment('human').if(variables.expanded)}
      }
    `
  }

});

Which runs fine for the root list. But as soon as I click on a ListItem and it is expanded, I get the following error:

Warning: RelayContainer: Expected prop 'human' supplied 'HumanList' 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.

I can't make much sense of it, since the data I'm passing is not mocked but directly fetched by Relay as can be seen in the HumanList comp.

like image 609
Gregor Weber Avatar asked Sep 10 '15 09:09

Gregor Weber


People also ask

What is data recursion?

Recursion in data structure is when a function calls itself indirectly or directly, and the function calling itself is known as a recursive function. It's generally used when the answer to a larger issue could be depicted in terms of smaller problems.

What is a recursive data structure?

A recursive data structure is a data structure that is partially composed of smaller or simpler instances of the same data structure. For example, linked lists and binary trees can be viewed as recursive data structures.

What is recursive example?

A classic example of recursionThe factorial of a number is computed as that number times all of the numbers below it up to and including 1. For example, factorial(5) is the same as 5*4*3*2*1 , and factorial(3) is 3*2*1 .

What you mean by recursive?

Definition of recursive 1 : of, relating to, or involving recursion a recursive function in a computer program. 2 : of, relating to, or constituting a procedure that can repeat itself indefinitely a recursive rule in a grammar.


1 Answers

The error indicates that the <HumanList> is being rendered before its data is ready.

class HumanListItem extends Component {
  onClickHead() {
    this.props.relay.setVariables({expanded: true});
    this.setState({expanded: true});  // <-- this causes the component to re-render before data is ready
  }

Rather than using state, you can instead look at the current value of the variables:

class HumanListItem extends Component {
  // no need for `state.expanded`

  render() {
    let {human} = this.props;

    let sublistHTML = '';
    if (this.props.relay.variables.expanded) {
      // `variables` are the *currently fetched* data
      // if `variables.expanded` is true, expanded data is fetched
      sublistHTML = <ul><HumanList human={human}/></ul>;
    }

    return (
      <li>
        <div onClick={this.onClickHead.bind(this)}>{human.name}</div>
        {sublistHTML}
      </li>
    );
  }

  onClickHead() {
    this.props.relay.setVariables({expanded: true});
    // no need for `setState()`
  }

}

HumanListItem.defaultProps = {viewer: {}};

export default Relay.createContainer(HumanListItem, {

  initialVariables: {
    expanded: false
  },

  fragments: {
    human: (variables) =>  Relay.QL`
      fragment on Human {
        name,
        ${HumanList.getFragment('human').if(variables.expanded)}
      }
    `
  }

});
like image 70
Joe Savona Avatar answered Nov 15 '22 04:11

Joe Savona