class Comment extends React.Component { render() { var {comment} = this.props; var {author} = comment; var savePending = this.props.relay.hasOptimisticUpdate(this.props.comment); return (
{author.name} {comment.text} {savePending && }
); } } Comment = Relay.createContainer(Comment, { fragments: { comment: () => Relay.QL` fragment on Comment { author { avatar, name } text } `, }, }); class Story extends React.Component { _augmentNumCommentsToShow = (delta) => { this.props.relay.setVariables({ numCommentsToShow: this.props.relay.variables.numCommentsToShow + delta, }); } _handleKeyDown = (e) => { if (e.keyCode === 13 && e.target.value != '') { // enter key Relay.Store.update( new AddCommentMutation({ story: this.props.story, text: e.target.value, viewer: this.props.viewer, }) ); this._augmentNumCommentsToShow(1); e.target.value = ''; } } _handleMoreCommentsClick = (e) => { e.preventDefault(); this._augmentNumCommentsToShow(5); } render() { var {story} = this.props; var {author, comments} = story; return (
{author.name}

{story.text}

{comments.pageInfo.hasPreviousPage && View previous comments }
); } } Story = Relay.createContainer(Story, { initialVariables: { numCommentsToShow: 3, }, fragments: { story: () => Relay.QL` fragment on Story { author { avatar, name } comments(last: $numCommentsToShow) { edges { node { id ${Comment.getFragment('comment')} } } pageInfo { hasPreviousPage } } text ${AddCommentMutation.getFragment('story')} } `, viewer: () => Relay.QL` fragment on Viewer { avatar id name ${AddCommentMutation.getFragment('viewer')} } `, }, }); class StoriesApp extends React.Component { _handleLoadMoreClick = () => { this.props.relay.setVariables({ numStoriesToLoad: this.props.relay.variables.numStoriesToLoad + 3, }); } render() { var {storyFeed} = this.props.viewer; return (
{storyFeed.pageInfo.hasNextPage && }
); } } StoriesApp = Relay.createContainer(StoriesApp, { initialVariables: { numStoriesToLoad: 3, }, fragments: { viewer: () => Relay.QL` fragment on Viewer { storyFeed(first: $numStoriesToLoad) { edges { node { id ${Story.getFragment('story')}, } } pageInfo { hasNextPage } } ${Story.getFragment('viewer')} } `, } }); class AddCommentMutation extends Relay.Mutation { static fragments = { story: () => Relay.QL` fragment on Story { id } `, viewer: () => Relay.QL` fragment on Viewer { id } `, }; getCollisionKey() { return `story-${this.props.story.id}`; } getMutation() { return Relay.QL`mutation{addComment}`; } getFatQuery() { return Relay.QL` fragment on AddCommentPayload { commentEdge story { comments } } `; } getConfigs() { return [{ type: 'RANGE_ADD', parentName: 'story', parentID: this.props.story.id, connectionName: 'comments', edgeName: 'commentEdge', rangeBehaviors: { '': 'append', }, }]; } getVariables() { return { storyId: this.props.story.id, text: this.props.text, }; } getOptimisticResponse() { return { commentEdge: { node: { author: { avatar: this.props.viewer.avatar, id: this.props.viewer.id, name: this.props.viewer.name, }, story: { id: this.props.story.id, }, text: this.props.text, }, }, }; } } class StoriesRoute extends Relay.Route { static params = {}; static queries = { viewer: (Component) => Relay.QL` query ViewerQuery { viewer { ${Component.getFragment('viewer')}, } } `, }; static routeName = 'Stories'; } ReactDOM.render( , mountNode );