Apollo useSubscription doesn't work for new windows - reactjs

This is my first time using useSub and I noticed that although my backend sends it's responses to the client (at least the console.log shows it is). The client using useSub doesn't do anything. I usually use subscribeToMore with query, but for this job I want to only get the most updated info. Is there a way to check if useSub connects correctly? Or is it broken in "#apollo/react-hooks": "^3.1.3"
Query
export const INCOMING_VIDEO_CHAT = gql`
subscription {
incomingVideoChat {
rn
p
}
}
`;
Client
const { data, loading } = useSubscription(INCOMING\_VIDEO\_CHAT, {
onSubscriptionData: ({ subscriptionData }) => {
console.log(subscriptionData);
}
});
Server:
module.exports = {
type: chatInfoType,
subscribe: () => pubsub.asyncIterator(INCOMING\_VIDEO\_CHAT),
async resolve(payload, { }, req) {
if (auth.isAuthenticated(req)) {
if (!payload) {
return;
}
const { userID, rn, p } = payload;
try {
if (req.id === userID) {
return { rn, p };
} else {
return;
}
} catch (e) {
throw new Error(e);
}
}
}
};

You can use Chrome Dev Tools to check if useSubscription connects correctly. In the Network tab of the Chrome DevTools, you should switch the filter to WS to see and debug your Apollo GraphQL subscription. You should read it.

Related

How to integrate Phantom wallet in react project?

I actually desire to integrate phantom wallets into my custom hand website. Since I'm new to the web 3, I've just used Metamask and am unsure of what Solana and Phantom Wallets are.
Write a provider and then wrap your _app with this provider:
import {
ConnectionProvider,
WalletProvider,
} from '#solana/wallet-adapter-react'
import { WalletModalProvider } from '#solana/wallet-adapter-react-ui'
import { PhantomWalletAdapter } from '#solana/wallet-adapter-wallets'
import { useMemo } from 'react'
const WalletConnectionProvider = ({ children }) => {
const endpoint = useMemo(() => 'https://api.devnet.solana.com', [])
const wallets = useMemo(() => [new PhantomWalletAdapter()], [])
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>{children}</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
)
}
export default WalletConnectionProvider
or you manually check for window.solana the way you connect to window.ethereum
const isWalletConnected = async () => {
try {
const { solana } = window;
if (solana) {
if (solana.isPhantom) {
console.log("phantom wallet found");
// When using this flag, Phantom will only connect and emit a connect event if the application is trusted. Therefore, this can be safely called on page load for new users, as they won't be bothered by a pop-up window even if they have never connected to Phantom before.
// if user already connected, { onlyIfTrusted: true }
const response = await solana.connect({ onlyIfTrusted: false });
console.log(
"public key",
response.publicKey.toString()
);
setWalletAddress(response.publicKey.toString());
} else {
alert("Please install phantom wallet");
}
}
} catch (error) {
console.log(error);
}
};

React-query: How to avoid triggering function call if query is empty?

I am using react-query to call an API. The call works well and is performed each time a query value is updated in an input field.
Unfortunately, it also triggers an API call even when the query is empty.
For example, when the user loads the app, the input (and hence query) will be blank.
How to trigger API calls only when there is a query?
Code
// API call
export async function myQuery(query) {
try {
const res = await ax.get("myapiurl", {
params: { query },
});
return res.data;
} catch {
return null;
}
}
// react-query
const { status, data } = useQuery(
["myquery", { query }],
() => myQuery(query)
);
There is an enabled flag in react-query for this exact use case.
Usage example
const { status, data } = useQuery(
["myquery", { query }],
() => myQuery(query).
{ enabled: !!query }
);
Docs for reference
You can achieve that with a simple if sentence:
// apicall
export async function myQuery(query) {
try {
const res = await ax.get("myapiurl", {
params: { query },
});
return res.data;
} catch {
return null;
}
}
// react-query
const { status, data } = useQuery(
["myquery", { query }],
() => {
if (query) {
return myQuery(query)
}
);

RelayObservable: Unhandled Error TypeError: Cannot read property 'subscribe' of undefined in React and Relay

I have followed the subscription tutorial on How to GraphQL React + Relay (https://relay.dev/docs/en/subscriptions) but still not working.
I'm using Relay Modern in my app and have successfully integrated query but not working the requestSubscription function.
Any help would be awesome.
My environment.js file:
function setupSubscription(
config,
variables,
cacheConfig,
observer,
) {
const query = config.text
const subscriptionClient = new SubscriptionClient('ws://192.168.1.19:8000/subscriptions', {reconnect: true});
const id = subscriptionClient.on({query, variables}, (error, result) => {
console.log(result,'result');
observer.onNext({data: result})
})
}
const network = Network.create(fetchQuery, setupSubscription)
const environment = new Environment({
network,
store
});
export default environment;
- My Subscription.js file:
const subscription = graphql`
subscription newVoteSubscription {
leaderboardUpdate {
id,
game_id,
event_id,
colarr,
rowarr
}
}
`;
function newVoteSubscription(callback) {
const variables = {};
return requestSubscription(environment, {
subscription: subscription,
variables: variables,
onError: (error)=> {
console.log(error, "error");
},
onNext: (res) => {
console.log(res,'onNext');
// callback();
},
updater: proxyStore => {
console.log(proxyStore,'proxyStore');
},
onCompleted: () => {
console.log('test');
},
});
}
export default newVoteSubscription;
I had trouble with the network as well. On Relay 7 using an Observable worked for me. This also handles error cases and the server closing the subscription.
const subscriptionClient = new SubscriptionClient('ws://192.168.1.19:8000/subscriptions', {reconnect: true})
function setupSubscription(
request,
variables,
cacheConfig,
) {
const query = request.text;
// Observable is imported from the relay-runtime package
return Observable.create(sink => {
const c = subscriptionClient.request({ query, variables }).subscribe(sink);
return c.unsubscribe;
});
}
I'm not sure why i've gone with the sink / unsubscribe approach, but this is what worked for me. As far as i remember the observable types used by relay and subscriptions-transport-ws were not compatible.
Also i'd advise you to hoist the new SubscriptionClient() call outside of the setupSubscription function as otherwise you'll open a new WebSocket for each subscription request.
I got the response, but now observer.onNext is undefined.
My updated code environment.js:
const setupSubscription = (config, variables, cacheConfig, observer) => {
const query = config.text
const subscriptionClient = new SubscriptionClient('ws://192.168.1.19:8000/subscriptions', {reconnect: true})
subscriptionClient.request({ query, variables }).subscribe((result) => {
observer.onNext({data: result})
});
return Observable.create(() => {
return subscriptionClient;
});
}
const environment = new Environment({
network: Network.create(fetchQuery, setupSubscription),
store: new Store(new RecordSource())
});

Apollo subscription issue - updateQuery not firing

I have a functioning web socket created with Apollo's WebSocketLink interface. I managed to subscribe to an event using subscribeToMore and a message is pushed by the server (can see it in the network tab). Unfortunately updateQuery function is never triggered. I wonder whether it's the message structure that is incorrect (therefore a wrong server implementation) or is it something wrong in my client code.
For reference I added the message sent from server:
and here the graphql config for my component:
import { graphql } from "react-apollo/index";
import Insights from 'components/insights/Insights';
import gql from "graphql-tag";
import { withRouter } from "react-router-dom";
import get from 'lodash/get';
const query = gql`
query CampaignInsights($campaignId: ID) {
campaigns (id: $campaignId) {
edges {
node {
insights {
campaignPlanningInsight {
campaign
plannedTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
liveTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
}
}
}
}
}
}
`;
const insightsSubscription = gql`
subscription onInsightsUpdated($campaignId: ID) {
campaignPlanningInsightUpdated(id: $campaignId) {
id
plannedTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
liveTotals {
totalOptimizationRules
totalOfferGroups
totalOffers
}
}
}
`;
const InsightsWithData = graphql(query, {
options: (props) => {
return {
variables: {
campaignId: props.match.params.campaignId
}
}
},
props: ({ data: { campaigns, subscribeToMore }, ownProps: { match }
}) => {
return {
insights: get(campaigns,
'edges[0].node.insights[0].campaignPlanningInsight', null),
subscribeToInsightsUpdate: () => {
return subscribeToMore({
document: insightsSubscription,
variables: {
campaignId: match.params.campaignId
},
updateQuery: (prev, { subscriptionData }) => {
debugger; // never gets here
if (!subscriptionData.data) {
return prev;
}
}
})
}
}
}
})(Insights);
export default withRouter(InsightsWithData);
I believe the issue might be the id of the graphql-ws websocket protocol.
That id needs to match the one sent by the frontend in the GQL_START message. Otherwise, the component won't re-render on a new message.
For more details, look into the subscription-transport-ws protocol

Apollo GraphQL Subscriptions

I'm having trouble with GraphQL subscriptions in Apollo. I want to subscribe to added "perspectives" on topics (basically added comments on posts), and I'm pretty sure I have the server set up correctly. The client is what's giving me trouble. (If this question looks familiar, I asked it before and thought I got an answer, but no go). Here is my subscription schema:
type Subscription {
perspectiveAdded: Perspective
}
schema {
query: RootQuery
mutation: Mutation
subscription: Subscription
}
My subscription resolver:
Subscription: {
perspectiveAdded(perspective) {
return perspective;
}
}
My subscriptionManager:
const pubsub = new PubSub();
const subscriptionManager = new SubscriptionManager({
schema,
pubsub,
setupFunctions: {
perspectiveAdded: (options, args) => {
perspectiveAdded: {
filter: (topic) => {
return topic
}
}
},
}
});
export { subscriptionManager, pubsub };
The last part of my addPerspective mutation that is (the event trigger for the subscription):
//...
return perspective.save((error, perspective) => {
if(error){
console.log(error);
}
//Publish it to Subscription channel
pubsub.publish('perspectiveAdded', perspective);
});
And then I've wired up the actual server to support subscriptions:
const PORT = process.env.PORT || 4000;
const server = createServer(app);
server.listen(PORT, ()=>{
new SubscriptionServer(
{
subscriptionManager: subscriptionManager,
onConnect: (connectionParams, webSocket) => {
console.log('Websocket connection established Lord Commander');
},
onSubscribe: (message, params, webSocket) => {
console.log("The client has been subscribed, Lord Commander", message, params);
},
onUnsubsribe: (webSocket) => {
console.log("Now unsubscribed, Lord Commander");
},
onDisconnect: (webSocket) => {
console.log('Now disconnected, Lord Commander');
}
},
{
server: server,
path: '/subscriptions',
});
console.log('Server is hot my Lord Commander!');
});
I've wired up the client correctly as well, because in my terminal I see the "Websocket connection established" message. The part I'm stumped about is how to actually call the subscription. According to the Apollo blog, I should be able to test the subscription in GraphiQL (since I'm using an apollo server, now graphql-server-express), but it says "Resolve function for \"Subscription.perspectiveAdded\" returned undefined".
For my component, I've tried to wire up 'subscribeToMore' but in the browser console, I'm getting an error object that says "Invalid params returned from onSubscribe! return values must be an object!" I'm not sure which object it is referring to.
Here's my subscription query called perspectiveSubscription:
export default gql`
subscription {
perspectiveAdded {
id
content
}
}
`;
And the wired up component:
constructor(props){
super(props);
this.state = {};
this.subscription = null;
}
componentWillReceiveProps(nextProps) {
if (!this.subscription && !nextProps.data.loading) {
let { subscribeToMore } = this.props.data
this.subscription = subscribeToMore(
{
document: perspectiveSubscription,
updateQuery: (previousResult, { subscriptionData }) => {
if(!subscriptionData.data){
console.log('no new subscription data');
return previousResult;
}
const newPerspective = subscriptionData.data.perspectiveAdded;
console.log(newPerspective);
return Object.assign({}, previousResult, newPerspective);
}
}
)
}
From here, I get a message in my terminal saying the client has been subscribed, but still I get the error object mentioned above. I've been pulling my hair out about this for days - do you guys see what I am missing here? Specifically, any ideas on the client side? Thanks everyone!
It seems like the server side is not correct, because the subscription is added and graphiql also does not deliver a correct result.
One thing that i suggest is that you check the channel definition:
const pubsub = new PubSub();
const subscriptionManager = new SubscriptionManager({
schema,
pubsub,
setupFunctions: {
perspectiveAdded: (options, args) => {
perspectiveAdded: {
filter: (perspective) => {
console.log(perspective); // check if object is correct
return true; // return true and not the object as long as you do not want to filter
}
}
},
}
});
export { subscriptionManager, pubsub };
And also check if the perspective object is saved and defined before the pubsub call.
And i think you also want to add a comment id for which the subscription should be working. On my side it looks more or less like in this post

Resources