4

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

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

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每个小部件使用的可行性,并将很快更新。

4

1 回答 1

4

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

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

联合类型应该允许您将“仪表板”类型描述为具有“小部件”列表。如果将“Widget”定义为联合类型,则可以拥有许多不同类型的 Widget,它们具有自己独特的字段和数据。

一旦你的服务器提供联合类型,你就可以编写一个 Dashboard 中继容器,如下所示:

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")}
        }
      }
    `},
  }
});

请注意在联合类型上定义的特殊“__typename”字段(有关详细信息,请参阅此问题)。然后,您的仪表板组件可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} />
  }
})

我很确定这会起作用,但我自己根本没有尝试过。根据我对 Relay 的了解(不是那么多),这可能是建模问题的最“Relay-ish”方式。

那有意义吗?

于 2015-12-24T16:09:56.900 回答