Required context `subscriptions` was not specified
h3llh0und opened this issue · comments
Hi,
i want to try out the subscriptions. But i got some errors while trying. Maybe you can help me:
This is the Error:
"warning.js:45 Warning: Failed Context Types: Required context subscriptions
was not specified in SubscriptionContainer(MessageList)
. Check the render method of Relay(props => ReactElement)
."
After this "this.props.subscriptions" is undefined.
Here is the Code:
import React from 'react';
import Relay from 'react-relay';
import auth from '../authentication';
import MessageTitle from './MessageTitle';
import * as RelaySubscriptions from 'relay-subscriptions';
class MessageList extends React.Component {
constructor() {
super();
this.getClassName = this.getClassName.bind(this);
}
componentDidMount() {
const subscribe = this.props.subscriptions.subscribe;
this._addSubscription = subscribe(
new AddMessageSubscription({user: this.props.user}),
'add_message'
);
}
componentWillUnmount() {
if (this._addSubscription) this._addSubscription.dispose();
}
getClassName(message) {
var className = 'card';
if (!message.isRead) {
className += ' card-inverse card-danger';
}
return className;
}
markAsRead(message) {
if (!message.isRead) {
var onFailure = (transaction) => {
var error = transaction.getError() || new Error('Mutation failed.');
self.setState({errorMessage: error.source.errors[0].message});
console.error(error);
};
var onSuccess = (response) => {
console.log(response);
};
Relay.Store.commitUpdate(new readMessageMutation(
{
message: message,
user: this.props.user
}), {onFailure, onSuccess}
);
}
}
render() {
return (
<div id="accordion">
{this.props.user.messages.map(message =>
<div className={this.getClassName(message)}>
<div className="card-block" role="tab" id={'heading' + message.messageId}>
<h5 className="card-title">
<a onClick={this.markAsRead.bind(this, message)} data-toggle="collapse"
data-parent="#accordion" href={'#collapse' + message.messageId} aria-expanded="false"
aria-controls={'collapse' + message.messageId}>
<MessageTitle message={message}></MessageTitle>
</a>
</h5>
</div>
<div id={'collapse' + message.messageId} className="panel-collapse collapse" role="tabpanel"
aria-labelledby={'heading' + message.messageId}>
<div className="card-block">
<div dangerouslySetInnerHTML={{__html: message.message}}></div>
</div>
</div>
</div>
)}
</div>
)
}
}
class AddMessageSubscription extends RelaySubscriptions.Subscription {
getSubscription() {
return Relay.QL`subscription {
addMessageSubscription {
clientMutationId,
user {
id
unreadMessageCount,
messages {
id,
messageId,
${MessageTitle.getFragment('message')},
message,
isRead
}
}
}
}`;
}
getVariables() {
return {};
}
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'user',
parentID: this.props.user.id,
connectionName: 'messages',
rangeBehaviors: () => 'append',
}];
}
}
class readMessageMutation extends Relay.Mutation {
getMutation() {
return Relay.QL`mutation {readMessage}`;
}
getVariables() {
return {messageId: this.props.message.messageId};
}
getConfigs() {
return [
{
type: 'FIELDS_CHANGE',
fieldIDs: {
user: this.props.user.id,
message: this.props.message.id
}
},
{
type: 'REQUIRED_CHILDREN',
// Forces these fragments to be included in the query
children: [Relay.QL`
fragment on readMessagePayload {
message{
id,
isRead
},
user {
id,
unreadMessageCount
}
}
`],
}
];
}
getFatQuery() {
return Relay.QL`
fragment on readMessagePayload {
message{
id,
isRead
},
user {
id,
unreadMessageCount
}
}
`;
}
getOptimisticResponse() {
return {
user: {
id: this.props.user.id,
unreadMessageCount: this.props.user.unreadMessageCount - 1
},
message: {
id: this.props.message.id,
isRead: true
}
};
}
}
export default Relay.createContainer(RelaySubscriptions.createSubscriptionContainer(MessageList), {
fragments: {
user: () => Relay.QL`
fragment on User {
id,
unreadMessageCount,
messages {
id,
messageId,
${MessageTitle.getFragment('message')},
message,
isRead
}
}
`,
}
});
Maybe someone can help?
Greetings Ronny
I think the problem is that you are missing to provide a SubscriptionProvider
component at the top of the component tree.
It's important to note that all SubscriptionContainers needs to be rendered under the SubscriptionProvider
.
Can you verify that you have a <SubscriptionProvider subscribe={} environment={}></SubscriptionProvider>
somewhere at the top of the tree?
In the todo app I have placed it in the Root
component which also is the component that handles all the communication with the server.
https://github.com/edvinerikson/relay-subscriptions/blob/master/examples/todo/js/components/Root.js#L67-L69
Also, please note that in your getSubscription
method please include the __typename
field on new nodes that gets added to the store, otherwise Relay can't handle the record correctly.
Thx, now it woks 👍