How to use QueryComponent with pollInterval and local state changes - react-apollo

i'm writing a small search app which starts a search over a RESTapi. Until the search is finished the api respond with a 260 status code or a 250 if the search is finished.
For this purpose i've written a component with a Query component which polls the RESTapi:
import React from 'react';
import gql from "graphql-tag";
import { Query } from "react-apollo";
export const GET_SEARCH_RESULTS = gql`
query GetSearchResults($sid: String!) {
getSearchResults(sid: $sid) {
status,
clusteredOffers {
id,
bookingId,
totalPrice
}
}
}
`;
export default class extends React.PureComponent {
client = null;
updateStatus = (data) => {
this.client.writeData({ data: { currentSearch: { status:
data.getSearchResults.status, __typename: 'CurrentSearch' }} })
};
render() {
const { sid } = this.props;
return (
<Query
query={GET_SEARCH_RESULTS}
variables={{ sid }}
pollInterval={500}
onCompleted={this.updateStatus}
>
{({ loading, error, data, stopPolling, client }) => {
this.client = client;
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
if(data.getSearchResults.status &&
data.getSearchResults.status !== "260") stopPolling();
return (
<div>
{data.getSearchResults.clusteredOffers.map(offer => (
<div key={offer.id} >
<div>{offer.id}</div>
<div>{offer.bookingId}</div>
<div>{offer.totalPrice}</div>
</div>
))}
</div>
)
}}
</Query>
)
}
}
In a parent component i also need the status of the current search. So i've added a clientState to my ApolloClient to write the remote data to the client cache:
import ApolloClient from "apollo-boost";
import Search from './components/Search';
const client = new ApolloClient({
uri: "http://localhost:4000/graphql",
clientState: {
defaults: {
currentSearch: {
__typename: 'CurrentSearch',
status: "0"
}
},
}
});
....
const GET_CURRENT_SEARCH_STATE = gql`
{
currentSearch #client {
status
}
}
`;
As u can see i'm using onCompleted inside my polling query component to write the current status to the local cache.
Does it make sense or should i read the status from the cached remote data? If so, how i get the cached remote data?
thank you!

Related

Apollo GraphQL react client: subscription

I was trying to make a little demo with GraphQL subscriptions and GraphQL Apollo client.
I already have my GraphQL API, but when I try to use Apollo client, it looks like it doesn't complete the websocket subscribe step:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery } from '#apollo/client';
import { split, HttpLink } from '#apollo/client';
import { getMainDefinition } from '#apollo/client/utilities';
import { GraphQLWsLink } from '#apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { useSubscription } from '#apollo/react-hooks'
import reportWebVitals from './reportWebVitals';
const httpLink = new HttpLink({
uri: 'https://mygraphql.api'
});
const wsLink = new GraphQLWsLink(createClient({
url: 'wss://mygraphql.api',
options: {
reconnect: true
}
}));
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
fetchOptions: {
mode: 'no-cors',
}
});
const FAMILIES_SUBSCRIPTION = gql`
subscription{
onFamilyCreated {
id
name
}
}
`;
function LastFamily() {
const { loading, error, data } = useSubscription(FAMILIES_SUBSCRIPTION, {
variables: { },
onData: data => console.log('new data', data)
});
if (loading) return <div>Loading...</div>;
if (error) return <div>Error!</div>;
console.log(data);
const family = data.onFamilyCreated[0];
return (
<div>
<h1>{family.name}</h1>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
(<ApolloProvider client={client}>
<div>
<LastFamily />
</div>
</ApolloProvider>));
reportWebVitals();
According to graphql-transport-ws, to accomplish a success call, it should call connection_init and subscribe message. But when I open Dev Tools, it only sends "connection_init"
I'm expecting this output:
What step should I add to accomplish a successful call using graphql-transport-ws?
P.s. I'm not a React Developer, just be kind.
The solutions I'm putting up are based on #apollo/server v.4, with expressMiddleware and mongodb/mongoose on the backend and subscribeToMore with updateQuery on the client-side instead of useSubscription hook. In light of my observations, I believe there may be some issues with your backend code that require refactoring. The transport library graphql-transport-ws has been deprecated and advise to use graphql-ws. The following setup also applies as of 12.2022.
Subscription on the backend
Install the following dependencies.
$ npm i #apollo/server #graphql-tools/schema graphql-subscriptions graphql-ws ws cors body-parser mongoose graphql express
Set up the db models, I will refer to mongodb using mongoose and it might look like this one e.g.
import mongoose from 'mongoose'
const Schema = mongoose.Schema
const model = mongoose.model
const FamilySchema = new Schema({
name: {
type: String,
unique: true, //optional
trim: true,
}
})
FamilySchema.virtual('id').get(function () {
return this._id.toHexString()
})
FamilySchema.set('toJSON', {
virtuals: true,
transform: (document, retObj) => {
delete retObj.__v
},
})
const FamilyModel = model('FamilyModel', FamilySchema)
export default FamilyModel
Setup schema types & resolvers; it might look like this one e.g.
// typeDefs.js
const typeDefs = `#graphql
type Family {
id: ID!
name: String!
}
type Query {
families: [Family]!
family(familyId: ID!): Family!
}
type Mutation {
createFamily(name: String): Family
}
type Subscription {
familyCreated: Family
}
`
// resolvers.js
import { PubSub } from 'graphql-subscriptions'
import mongoose from 'mongoose'
import { GraphQLError } from 'graphql'
import FamilyModel from '../models/Family.js'
const pubsub = new PubSub()
const Family = FamilyModel
const resolvers = {
Query: {
families: async () => {
try {
const families = await Family.find({})
return families
} catch (error) {
console.error(error.message)
}
},
family: async (parent, args) => {
const family = await Family.findById(args.familyId)
return family
},
Mutation: {
createFamily: async (_, args) => {
const family = new Family({ ...args })
try {
const savedFamily = await family.save()
const createdFamily = {
id: savedFamily.id,
name: savedFamily.name
}
// resolvers for backend family subscription with object iterator FAMILY_ADDED
pubsub.publish('FAMILY_CREATED', { familyCreated: createdFamily })
return family
} catch (error) {
console.error(error.message)
}
}
},
Subscription: {
familyCreated: {
subscribe: () => pubsub.asyncIterator('FAMILY_CREATED'),
}
},
Family: {
id: async (parent, args, contextValue, info) => {
return parent.id
},
name: async (parent) => {
return parent.name
}
}
}
export default resolvers
At the main entry server file (e.g. index.js) the code might look like this one e.g.
import dotenv from 'dotenv'
import { ApolloServer } from '#apollo/server'
import { expressMiddleware } from '#apollo/server/express4'
import { ApolloServerPluginDrainHttpServer } from '#apollo/server/plugin/drainHttpServer'
import { makeExecutableSchema } from '#graphql-tools/schema'
import { WebSocketServer } from 'ws'
import { useServer } from 'graphql-ws/lib/use/ws'
import express from 'express'
import http from 'http'
import cors from 'cors'
import bodyParser from 'body-parser'
import typeDefs from './schema/tpeDefs.js'
import resolvers from './schema/resolvers.js'
import mongoose from 'mongoose'
dotenv.config()
...
mongoose.set('strictQuery', false)
let db_uri
if (process.env.NODE_ENV === 'development') {
db_uri = process.env.MONGO_DEV
}
mongoose.connect(db_uri).then(
() => {
console.log('Database connected')
},
(err) => {
console.log(err)
}
)
const startGraphQLServer = async () => {
const app = express()
const httpServer = http.createServer(app)
const schema = makeExecutableSchema({ typeDefs, resolvers })
const wsServer = new WebSocketServer({
server: httpServer,
path: '/',
})
const serverCleanup = useServer({ schema }, wsServer)
const server = new ApolloServer({
schema,
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose()
},
}
},
},
],
})
await server.start()
app.use(
'/',
cors(),
bodyParser.json(),
expressMiddleware(server)
)
const PORT = 4000
httpServer.listen(PORT, () =>
console.log(`Server is now running on http://localhost:${PORT}`)
)
}
startGraphQLServer()
Subscription on the CRA frontend
Install the following dependencies.
$ npm i #apollo/client graphql graphql-ws
General connection setup e.g.
// src/client.js
import { ApolloClient, HttpLink, InMemoryCache, split } from '#apollo/client'
import { getMainDefinition } from '#apollo/client/utilities'
import { defaultOptions } from './graphql/defaultOptions'
import { GraphQLWsLink } from '#apollo/client/link/subscriptions'
import { createClient } from 'graphql-ws'
...
const baseUri = process.env.REACT_APP_BASE_URI // for the client
const wsBaseUri = process.env.REACT_APP_WS_BASE_URI // for the backend as websocket
const httpLink = new HttpLink({
uri: baseUri,
})
const wsLink = new GraphQLWsLink(
createClient({
url: wsBaseUri
})
)
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink,
httpLink
)
const client = new ApolloClient({
cache: new InMemoryCache(),
link: splitLink,
})
export default client
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import client from './client'
import { ApolloProvider } from '#apollo/client'
import App from './App'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>
)
Define the operations types for the client: queries, mutation & subscription e.g.
// src/graphql.js
import { gql } from '#apollo/client'
// Queries
export const FAMILIES = gql`
query Families {
families {
id
name
}
}
`
export const FAMILY = gql`
query Family($familyId: ID) {
family {
id
name
}
}
`
// Mutation
export const CREATE_FAMILY = gql`
mutation createFamily($name: String!) {
createFamily(name: $name) {
id
name
}
}
`
// Subscription
export const FAMILY_SUBSCRIPTION = gql`
subscription {
familyCreated {
id
name
}
}
Components, it might look like this one e.g.
Apollo's useQuery hook provides us with access to a function called subscribeToMore. This function can be destructured and used to act on new data that comes in via subscription. This has the result of rendering our app real-time.
The subscribeToMore function utilizes a single object as an argument. This object requires configuration to listen for and respond to subscriptions.
At the very least, we must pass a subscription document to the document key in this object. This is a GraphQL document in which we define our subscription.
We can a updateQuery field that can be used to update the cache, similar to how we would do in a mutation.
// src/components/CreateFamilyForm.js
import { useMutation } from '#apollo/client'
import { CREATE_FAMILY, FAMILIES } from '../graphql'
...
const [createFamily, { error, loading, data }] = useMutation(CREATE_FAMILY, {
refetchQueries: [{ query: FAMILIES }], // be sure to refetchQueries after mutation
})
...
// src/components/FamilyList.js
import React, { useEffect, useState } from 'react'
import { useQuery } from '#apollo/client'
import { Families, FAMILY_SUBSCRIPTION } from '../graphql'
const { cloneDeep, orderBy } = pkg
...
export const FamilyList = () => {
const [families, setFamilies] = useState([])
const { loading, error, data, subscribeToMore } = useQuery(Families)
...
useEffect(() => {
if (data?.families) {
setFamilies(cloneDeep(data?.families)) // if you're using lodash but it can be also setFamilies(data?.families)
}
}, [data?.families])
useEffect(() => {
subscribeToMore({
document: FAMILY_SUBSCRIPTION,
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev
const newFamily = subscriptionData.data.familyCreated
if (!prev.families.find((family) => family.id === newFamily.id)) {
return Object.assign({}, prev.families, {
families: [...prev.families, newFamily],
})
} else {
return prev
}
},
})
}, [subscribeToMore])
const sorted = orderBy(families, ['names'], ['desc']) // optional; order/sort the list
...
console.log(sorted)
// map the sorted on the return statement
return(...)
END. Hard-coding some of the default resolvers are useful for ensuring that the value that you expect will returned while avoiding the return of null values. Perhaps not in every case, but for fields that refer to other models or schema.
Happy coding!

Dynamic routing using graphQL in a Next.js app

I'm building a webpage that consumes the spaceX graphQL api, using apollo as a client. On the landing page I want to display a 'launches' card, that when clicked on, directs to a new page with details about that particular launch, as below:
index.js
import { ApolloClient, InMemoryCache, gql } from "#apollo/client"
import Link from 'next/link'
export const getStaticProps = async () => {
const client = new ApolloClient({
uri: 'https://api.spacex.land/graphql/',
cache: new InMemoryCache()
})
const { data } = await client.query({
query: gql`
query GetLaunches {
launchesPast(limit: 10) {
id
mission_name
launch_date_local
launch_site {
site_name_long
}
links {
article_link
video_link
mission_patch
}
rocket {
rocket_name
}
}
}
`
});
return {
props: {
launches: data.launchesPast
}
}
}
export default function Home({ launches }) {
return (
<div>
{launches.map(launch => {
return(
<Link href = {`/items/${launch.id}`} key = {launch.id}>
<a>
<p>{launch.mission_name}</p>
</a>
</Link>
)
})}
</div>
)
}
I've set up a new page items/[id].js to display information about individual launches, but this is where the confusion is. Using a standard REST api I'd simply use fetch, then append the id to the end of the url to retrieve the desired data. However I'm not sure how to do the equivalent in graphQL, using the getStaticPaths function. Any suggestions?
Here's items/[id]/js, where I'm trying to render the individual launch data:
import { ApolloClient, InMemoryCache, gql } from "#apollo/client"
export const getStaticPaths = async () => {
const client = new ApolloClient({
uri: "https://api.spacex.land/graphql/",
cache: new InMemoryCache(),
});
const { data } = await client.query({
query: gql`
query GetLaunches {
launchesPast(limit: 10) {
id
}
}
`,
});
const paths = data.map((launch) => {
return {
params: { id: launch.id.toString() },
};
});
return {
paths,
fallback:false
}
};
export const getStaticProps = async (context) => {
const id = context.params.id
// not sure what to do here
}
const Items = () => {
return (
<div>
this is items
</div>
);
}
export default Items;
for getStaticPaths
export const getStaticPaths = async () => {
const { data } = await client.query({
query: launchesPastQuery, // this will query the id only
});
return {
paths: data.CHANGE_THIS.map((param) => ({
params: { id: param.id },
})),
fallback: false,
};
};
CHANGE_THIS is the Query Type that follows data in the JSON response.
for getStaticProps
export const getStaticProps = async ({
params,
}) => {
const { data } = await client.query({
query: GetLaunchPastByID ,
variables: { LaunchID: params.id, idType: "UUID" }, // the idType is optional, and the LaunchID is what you'll use for querying by it*
});
return {
props: {
launchesPast: data.CHANGE_THIS,
},
};
The launchPastQueryByID is like:
const GetLaunchPastByID = gql`
query LaunchPastByID($LaunchID: UUID!) { // UUID is id type
CHANGE_THIS(id: $LaunchID) {
id
//...
}
}
`;
sorry for not giving you the correct queries, spacex.land is currently down.

Cannot destructure property of {intermediate value} as it is undefined

I have just started using graphql for the first time as I have integrated my NEXTJS app with strapi. But I have received this error message Cannot destructure property 'data' of '(intermediate value)' as it is undefined.
I followed this tutorial - enter link description here
Just modified it to what I wanted. This is my graphql:
query {
posts {
data {
attributes {
heading
}
}
}
}
And this is my vs code:
export async function getStaticProps() {
const client = new ApolloClient({
url: 'http://localhost:1337/graphql/',
cache: new InMemoryCache(),
})
const { data } = await client.query({
query: gql`
query {
posts {
data {
attributes {
heading
}
}
}
}
`,
})
return {
props: {
posts: data.posts,
},
}
}
FULL CODE:
import { ApolloClient, InMemoryCache, gql } from '#apollo/client'
export default function Blog({ posts }) {
console.log('posts', posts)
return (
<div>
{posts.map(post => {
return (
<div>
<p>{posts.heading}</p>
</div>
)
})}
</div>
)
}
export async function getStaticProps() {
const client = new ApolloClient({
url: 'http://localhost:1337/graphql/',
cache: new InMemoryCache(),
})
const { data } = await client.query({
query: gql`
query {
posts {
data {
attributes {
heading
}
}
}
}
`,
})
return {
props: {
posts: data.posts,
},
}
}
I really don't know where to begin with this.
Firstly check whether or not you are receiving empty data from API.
If its array, check its length or use methods like Array.isArray(myArray).
If its object, make a function like this to check objects.
function isObjectEmpty(obj) {
return (
!!obj && // 👈 null and undefined check
Object.keys(obj).length === 0 &&
obj.constructor === Object
)
}
export default isObjectEmpty
if the data is empty return notFound prop as true to show your 404 page.
// This function gets called at build time
export async function getStaticProps({ params, preview = false }) {
// fetch posts
// check validity data
return isObjectEmpty(pageData)
? { notFound: true }
: {
props: {
posts
}
}
}
Secondly add a failsafe mechanism like the use of optional-chaining to securely access nested values/properties.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
export default function Blog({ posts }) {
console.log('posts', posts)
return (
<div>
{posts?.length && posts?.map(post => {
return (
<div>
<p>{posts?.heading}</p>
</div>
)
})}
</div>
)
}
I was running into the same error while testing using Jest.
It turns out I was mocking all of graphql, but I had to specifically mock the return value.

why does my graphql return an empty array?

I am newbie with graphql. I have a front-end project (nextjs) and back-end(strapi).
This is my code
import { ApolloClient, InMemoryCache, gql } from '#apollo/client'
export default function Blog({ posts }) {
console.log('posts', posts)
return (
<div>
{posts.map(post => {
return (
<div>
<p>{posts.heading}</p>
</div>
)
})}
</div>
)
}
export async function getStaticProps() {
const client = new ApolloClient({
url: 'http://localhost:1337/graphql/',
cache: new InMemoryCache(),
})
const { data } = await client.query({
query: gql`
query {
posts {
data {
attributes {
heading
}
}
}
}
`,
})
return {
props: {
posts: data.posts,
},
}
}
alongside this, I also get this message "cannot destructure property of intermediate value". Does anybody know why, i'm sure the code is correct.
Firstly i recommend creating a folder in root and put all graphql codes and you have also a syntax error:
let me give you an example:
/index.js
import { ApolloClient, InMemoryCache, gql } from "#apollo/client";
const client = new ApolloClient({
uri: API,
cache: new InMemoryCache(),
defaultOptions: {
query: {
fetchPolicy: "network-only",
},
},
});
//get category ids
const getCatIds = async () => {
try {
const { data } = await client.query({
query: gql`
query categoriesId {
categories(sort: "id:ASC") {
id
name
}
}
`,
});
return data.categories;
} catch {
console.log("error appolo");
}
};
export {getCatIds}

How to integrate Multiple clients using graphql hooks in react hooks

I wanna integrate multiple clients in my react-hooks application. I'm using graphql-hooks via Apollo client we have a module to integrate multiple clients
Following is the link Apollo graphql multiple client
`https://www.npmjs.com/package/#titelmedia/react-apollo-multiple-clients`
Following I'm using for graphql hooks
https://www.npmjs.com/package/graphql-hooks
How do we achieve the same for graphql-hooks?
My requirement is depending on the selection of the radio button I need to switch between these multiple clients all that in one component using more than one client.
In my app I'm using graphql-hook we wrap the component to the client here the same component has functionality wherein depending on the select one of the radio buttons the client must be switch.I'm having one client but need to integrate multiple clients I googled but i've not found so have questioned here.
Is this possible?
Can anyone please help out there
We can use new GraphQLClient() multiple times to get multiple graphql clients
Here is a good approach and you can follow it: https://www.loudnoises.us/next-js-two-apollo-clients-two-graphql-data-sources-the-easy-way/
I have two middleware graphql servers running. One for the app one for analytics. I do it in two different ways according to my need as below...
CubeClient.jsx
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { SchemaLink } from "apollo-link-schema";
import { makeExecutableSchema } from "graphql-tools";
const cache = new InMemoryCache();
const defaultDashboardItems = [{"vizState":"{\"query\":{\"measures\":[\"Product.count\"],\"timeDimensions\":[{\"dimension\":\"Product.createdAt\"}],\"dimensions\":[\"Producttype.name\"],\"filters\":[],\"order\":{}},\"chartType\":\"bar\",\"orderMembers\":[{\"id\":\"Product.count\",\"title\":\"Product Count\",\"order\":\"none\"},{\"id\":\"Producttype.name\",\"title\":\"Producttype Name\",\"order\":\"none\"},{\"id\":\"Product.createdAt\",\"title\":\"Product Created at\",\"order\":\"none\"}],\"pivotConfig\":{\"x\":[\"Producttype.name\"],\"y\":[\"measures\"],\"fillMissingDates\":true,\"joinDateRange\":false},\"shouldApplyHeuristicOrder\":true,\"sessionGranularity\":null}","name":"Product Types","id":"2","layout":"{\"x\":0,\"y\":0,\"w\":24,\"h\":8}"},{"vizState":"{\"query\":{\"measures\":[\"Sale.total\"],\"timeDimensions\":[{\"dimension\":\"Sale.createdAt\",\"granularity\":\"day\"}],\"dimensions\":[\"Customer.firstname\"],\"order\":{\"Sale.total\":\"desc\"}},\"chartType\":\"area\",\"orderMembers\":[{\"id\":\"Sale.total\",\"title\":\"Sale Total\",\"order\":\"desc\"},{\"id\":\"Customer.firstname\",\"title\":\"Customer Firstname\",\"order\":\"none\"},{\"id\":\"Sale.createdAt\",\"title\":\"Sale Created at\",\"order\":\"none\"}],\"pivotConfig\":{\"x\":[\"Sale.createdAt.day\"],\"y\":[\"Customer.firstname\",\"measures\"],\"fillMissingDates\":true,\"joinDateRange\":false},\"shouldApplyHeuristicOrder\":true}","name":"Sale Total","id":"3","layout":"{\"x\":0,\"y\":8,\"w\":24,\"h\":8}"}]
export function getCubeClient(location){
const getDashboardItems = () => {
// return JSON.parse(window.localStorage.getItem("dashboardItems")) || defaultDashboardItems;
return defaultDashboardItems
}
const setDashboardItems = items => {
return window.localStorage.setItem("dashboardItems", JSON.stringify(items));
}
const nextId = () => {
const currentId = parseInt(window.localStorage.getItem("dashboardIdCounter"), 10) || 1;
window.localStorage.setItem("dashboardIdCounter", currentId + 1);
return currentId.toString();
};
const toApolloItem = i => ({ ...i, __typename: "DashboardItem" });
const typeDefs = `
type DashboardItem {
id: String!
layout: String
vizState: String
name: String
}
input DashboardItemInput {
layout: String
vizState: String
name: String
}
type Query {
dashboardItems: [DashboardItem]
dashboardItem(id: String!): DashboardItem
}
type Mutation {
createDashboardItem(input: DashboardItemInput): DashboardItem
updateDashboardItem(id: String!, input: DashboardItemInput): DashboardItem
deleteDashboardItem(id: String!): DashboardItem
}
`;
const schema = makeExecutableSchema({
typeDefs,
resolvers: {
Query: {
dashboardItems() {
const dashboardItems = getDashboardItems();
return dashboardItems.map(toApolloItem);
},
dashboardItem(_, { id }) {
const dashboardItems = getDashboardItems();
return toApolloItem(dashboardItems.find(i => i.id.toString() === id));
}
},
Mutation: {
createDashboardItem: (_, { input: { ...item } }) => {
const dashboardItems = getDashboardItems();
item = { ...item, id: nextId(), layout: JSON.stringify({}) };
dashboardItems.push(item);
setDashboardItems(dashboardItems);
return toApolloItem(item);
},
updateDashboardItem: (_, { id, input: { ...item } }) => {
const dashboardItems = getDashboardItems();
item = Object.keys(item)
.filter(k => !!item[k])
.map(k => ({
[k]: item[k]
}))
.reduce((a, b) => ({ ...a, ...b }), {});
const index = dashboardItems.findIndex(i => i.id.toString() === id);
dashboardItems[index] = { ...dashboardItems[index], ...item };
setDashboardItems(dashboardItems);
return toApolloItem(dashboardItems[index]);
},
deleteDashboardItem: (_, { id }) => {
const dashboardItems = getDashboardItems();
const index = dashboardItems.findIndex(i => i.id.toString() === id);
const [removedItem] = dashboardItems.splice(index, 1);
setDashboardItems(dashboardItems);
return toApolloItem(removedItem);
}
}
}
});
return new ApolloClient({
cache,
uri: "http://localhost:4000",
link: new SchemaLink({
schema
})
});
}
Dashboard.jsx
import { getCubeClient } from './CubeClient';
import { Query } from "react-apollo";
let cubeClient = getCubeClient()
const Dashboard = () => {
const dashboardItem = item => (
<div key={item.id} data-grid={defaultLayout(item)}>
<DashboardItem key={item.id} itemId={item.id} title={item.name}>
<ChartRenderer vizState={item.vizState} />
</DashboardItem>
</div>
);
return(
<Query query={GET_DASHBOARD_ITEMS} client={cubeClient}>
{({ error, loading, data }) => {
if (error) return <div>"Error!"</div>;
if (loading) return (
<div className="alignCenter">
<Spinner color="#be97e8" size={48} sizeUnit="px" />
</div>
);
if (data) {
return (<DashboardView t={translate} dashboardItems={data && data.dashboardItems}>
{data && data.dashboardItems.map(deserializeItem).map(dashboardItem)}
</DashboardView>)
} else {
return <div></div>
}
}}
</Query>
)
};
Explore.jsx
import { useQuery } from "#apollo/react-hooks";
import { withRouter } from "react-router-dom";
import ExploreQueryBuilder from "../components/QueryBuilder/ExploreQueryBuilder";
import { GET_DASHBOARD_ITEM } from "../graphql/queries";
import TitleModal from "../components/TitleModal.js";
import { isQueryPresent } from "#cubejs-client/react";
import PageHeader from "../components/PageHeader.js";
import ExploreTitle from "../components/ExploreTitle.js";
import { PageLayout } from '#gqlapp/look-client-react';
import Helmet from 'react-helmet';
import { translate } from '#gqlapp/i18n-client-react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faAngleRight, faTrashAlt, faPlusCircle } from '#fortawesome/free-solid-svg-icons';
import settings from '#gqlapp/config';
import PropTypes from 'prop-types';
import { getCubeClient } from './CubeClient';
let cubeClient = getCubeClient()
const Explore = withRouter(({ history, location,t }) => {
const [addingToDashboard, setAddingToDashboard] = useState(false);
const params = new URLSearchParams(location.search);
const itemId = params.get("itemId");
const { loading, error, data } = useQuery(GET_DASHBOARD_ITEM, {
client: cubeClient,
variables: {
id: itemId
},
skip: !itemId
});
Titlemodal.jsx
import React from "react";
import { Modal, Input } from "antd";
import { useMutation } from "#apollo/react-hooks";
import { GET_DASHBOARD_ITEMS } from "../graphql/queries";
import {
CREATE_DASHBOARD_ITEM,
UPDATE_DASHBOARD_ITEM
} from "../graphql/mutations";
import { getCubeClient } from '../containers/CubeClient';
let cubeClient = getCubeClient()
const TitleModal = ({
history,
itemId,
titleModalVisible,
setTitleModalVisible,
setAddingToDashboard,
finalVizState,
setTitle,
finalTitle
}) => {
const [addDashboardItem] = useMutation(CREATE_DASHBOARD_ITEM, {
client: cubeClient,
refetchQueries: [
{
query: GET_DASHBOARD_ITEMS
}
]
});
const [updateDashboardItem] = useMutation(UPDATE_DASHBOARD_ITEM, {
client: cubeClient,
refetchQueries: [
{
query: GET_DASHBOARD_ITEMS
}
]
});
queries and mutations.js
import gql from "graphql-tag";
export const GET_DASHBOARD_ITEMS = gql`
query GetDashboardItems {
dashboardItems {
id
layout
vizState
name
}
}
`;
export const GET_DASHBOARD_ITEM = gql`
query GetDashboardItem($id: String!) {
dashboardItem(id: $id) {
id
layout
vizState
name
}
}
`;
export const CREATE_DASHBOARD_ITEM = gql`
mutation CreateDashboardItem($input: DashboardItemInput) {
createDashboardItem(input: $input) {
id
layout
vizState
name
}
}
`;
export const UPDATE_DASHBOARD_ITEM = gql`
mutation UpdateDashboardItem($id: String!, $input: DashboardItemInput) {
updateDashboardItem(id: $id, input: $input) {
id
layout
vizState
name
}
}
`;
export const DELETE_DASHBOARD_ITEM = gql`
mutation DeleteDashboardItem($id: String!) {
deleteDashboardItem(id: $id) {
id
layout
vizState
name
}
}
`;

Resources