RelayJS:子组件查询任意数据

时间:2015-12-22 08:18:40

标签: reactjs relayjs graphql

我正在构建一个包含许多不同小部件的仪表板。用户可以添加和删除任何窗口小部件,并按照他们喜欢的顺序放置它们。每个小部件都有自己的数据要求。构建容器层次结构的正确Relay方法是什么?

为了提供一些上下文,这是迄今为止的架构:

Widget是一个接收配置对象并相应地呈现相应组件的组件。

class Widget extends React.Component {
  render() {
    const {widget} = this.props;
    // widgetMap is a map that maps string to React component
    const ActualWidget = widgetMap.get(widget.component);

    return (
      <ActualWidget data={widget.data} />
    );
  }
}

export default Relay.createContainer(Widget, {
  fragments: {
    data: () => Relay.QL`
      fragment on ??? {
        # What should be here since we don't know what will be rendered?
      }
    `
  }
});

Dashboard组件包含许多由用户添加的小部件。

class Dashboard extends React.Component {
  renderWidgets = () => {
    return this.props.widgets.map(widget => {
      return <Widget widget={widget}/>;
    });
  };

  render() {
    return (
      <div>
        <span>Hello, {this.props.user.name}</span>
        {this.renderWidgets()}
      </div>
    );
  }
}

export default Relay.createContainer(Dashboard, {
  fragments: {
    // `user` fragment is used by Dashboard
    user: () => Relay.QL`
      fragment on User {
        name
      }
    `,
    // Each widget have different fragment,
    // So what should be here?
  }
});

更新

我试图让每个ActualWidget成为观众的一个领域。因此架构有点像这样:

type Viewer {
  widget1: Widget1
  widget2: Widget2
}

type Widget1 {
  name,
  fieldOnlyForWidget1
}

type Widget2 {
  name,
  fieldOnlyForWidget2
}

然后,对于我的Widget容器,我尝试动态插入片段。

export default Relay.createContainer(Widget, {
  initialVariables: {
    component: 'Widget1' // Trying to set the string here
  }

  fragments: {
    data: (variables) => Relay.QL`
      fragment on Viewer { # We now know it is Viewer type
        # This didn't work because `variables.component` is not string! :(
        ${widgetMap.get(variables.component).getFragment('viewer')}
      }
    `
  }
});

这不起作用。我相信Relay静态解析了QL,因此它无法构成动态片段。然而,这只是我的猜测。

我正在测试为每个小部件使用RootContainer的可行性,并会尽快更新。

1 个答案:

答案 0 :(得分:4)

我认为这里的根本问题是你在尝试让客户端根据服务器给出的一些数据(小部件名称)在运行时决定要请求的数据类型。这将始终是一个两步过程,因此您不能将子片段嵌套在父级中以进行单次提取;因此,一旦您知道自己需要什么,就需要设置一个新的RootContainer或其他内容来启动一组新的查询。

但是,我认为您可以通过在图表本身中编码窗口小部件信息,使用常规嵌套将其作为单次提取。这里的技巧是使用GraphQL联合类型:docs

联盟类型应该允许您描述您的&#34;仪表板&#34;输入&#34; Widgets&#34;列表。如果您定义&#34; Widget&#34;作为联合类型,您可以拥有许多不同类型的小部件,这些小部件具有自己独特的字段和数据。

一旦您的服务器提供联合类型,您就可以编写一个看起来像这样的仪表板中继容器:

var Dashboard = Relay.createContainer(DashboardComponent, {
  fragments: {
    dashboard: () => { console.log("dashboard query"); return Relay.QL`
      fragment on Dashboard {
        widgets {
          _typename
          ${FooWidget.getFragment("widget")}
          ${BarWidget.getFragment("widget")}
        }
      }
    `},
  }
});

注意特殊&#34; __ typename&#34;在联合类型上定义的字段(有关详细信息,请参阅this question)。然后,您的仪表板组件可以使用this.props.dashboard.widgets遍历所有小部件,并根据__typename创建相应的小部件,如下所示:

var widgets = this.props.dashboard.widgets.map((widget) => {
  if (widget.__typename == "FooWidget") {
    return <FooWidget widget={widget} />
  } else if (widget.__typename == "BarWidget") {
    return <CountdownWidget widget={widget} />
  }
})

我很确定这会奏效,但我自己并没有尝试过。根据我对接力的理解(这不是那么多),这可能是最多的接力和接力#34;模拟你的问题的方法。

这有意义吗?