What is the ideal way to use subscription with react apollo GraphQL - reactjs

I am working on a react project which uses GraphQl for API integration, which I did perfectly.
Now I am working on a module where I need to use subscription.
What I am doing
I have charts to show one UI, which I have created successfully using ReCharts.
So initially I a getting data from query and displaying it on UI which works totally fine.
Than as per requirement There is some live data which I am getting through subscription i.e web socket
So what I am doing is I stored query data in a state and when I got the live data I am appending the live data to that state and UI is updating correctly.
Issue I am facing
As I have many charts on UI so I am conditionally checking for which chart I am getting data and than appending to the particular one.
But what happens is when I got a lot of data which is coming every 2-6 seconds it hangs the browser after sometime, like after 10 minutes or 15 minutes, which is bit frustrating.
I don't know What is wrong in this, may be my approach is not good or something else
My code what I did
// below query is for getting list of factoories
//const { loading: loadingFactoryy, data: factoryList } = useQuery( FACTORYLIST);
// from above query I ll get data like below state
// as I am not connected to backend so I am taking data statically
const [factoryList, setFactoryList] = useState([
{
factoryId: 16,
factoryName: "factory one",
__typename: "user_factoryNames"
},
{
factoryId: 17,
factoryName: "factory two",
__typename: "user_factoryNames"
},
{
factoryId: 18,
factoryName: "factory Three",
__typename: "user_factoryNames"
},
{
factoryId: 19,
factoryName: "factory four",
__typename: "user_factoryNames"
}
]);
My Machine code
// below query to get the machines for each factories, if i click on
//factoryone it will give me all machines of factory one
// const [
// getMachines,
// { loading: loadingMachines, data: machineData },
// ] = useLazyQuery(FACTORYMACHINEBYID);
//I am using lazyquery so when I click I ll get the data
// useEffect(() => { this will run when page loades first time
// if (factoryList !== undefined) {
// if (factoryList.getUserFactorydNames.length > 0) {
// getInitialDataFun({
// variables: {
// factoryId:
// factoryList?.getUserFactorydNames[parseInt(activeDashboard)]
// ?.factoryId,
// },
// });
// }
// }
// }, [active_tab, factoryId, getInitialDataFun]);
//all functions for factories click
let active_tab = localStorage.getItem("active_tab") || 0;
const [active_menu, setActive_menu] = useState(Number(active_tab));
const [machineList, setmachineList] = useState([
{
chartId: 12,
chartBame: "machine1",
data: [
{
dValue: 5,
dName: "Data1"
},
{
dValue: 10,
dName: "Data2"
}
]
},
{
chartId: 13,
chartBame: "machine2",
data: [
{
dValue: 5,
dName: "Data1"
},
{
dValue: 10,
dName: "Data2"
}
]
}
]);
My subscription code
// my subscription code
// const {
// data: LiveDataMachine,
// error: errorLiveDataMachine,
// } = useSubscription(LIVEDATAMACHINEFUN, {
// variables: { topic: { topic: 'livemessage/' } },
// });
const factoryClick = (li, ind) => {
setActive_menu(ind);
localStorage.setItem("active_tab", ind);
};
//My live data looks like below
// {
// "chartId": 16,
// "chartBame": "machine1",
// "data": [
// {
// "dValue": 7,
// "dName": "Data1"
// },
// {
// "dValue": 18,
// "dName": "Data2"
// }
// ]
// }
// So what I am doing is whenever I am getting the live data
//I am looping it with main machine data and checking for chartId
//once it matches I am appending the data like below
// useEffect(() => {
// if(machineList){
// machineData.map((li,ind)=>{
// if(LiveDataMachine.chartId===li.chartId){
// setmachineList(machineList=>[...machineList,LiveDataMachine])
// }
// })
// }
// }, [LiveDataMachine]);
// but what is hapenning is it is updating the UI so fast that my browser is getting hanged
return (
<div className="App">
<FactoryComp
factoryData={factoryList}
active_menu={active_menu}
factoryClick={factoryClick}
/>
<hr />
<MachinesComp machineData={machineList} />
</div>
above is how I am writing my code and managing GraphQl subscription, I don't know what I am doing wrong which is making my browser hangs alot
here I am looking for a correct approach by which I can use subscription with my query.
Here is my working code I have written everything as a comment for better uderstanding

Looks like you are calling setmachineList in the if inside a render. It will endlessly cause component updates, thereby cycling it. Perhaps it is better to move this logic to useEffect?
useEffect(() => {
if(machineList){
machineData.forEach((li,ind)=>{
if(LiveDataMachine.chartId===li.chartId){
setmachineList(machineList=>[...machineList,LiveDataMachine])
}
})
}
}, [LiveDataMachine]);

Related

Gatsby-plugin-sitemap, custom config, need to integrate pages and markdown using custom resolvePages and Serialize, what does this line of code do?

just starting with javascript and react thanks to Gatsby so excuse me if this is a total newbie question. Also just starting with posting on stackoverflow, usually just consuming content, sorry about that and if my post is incomplete or unclear in anyway.
I am building a website using GatsbyJs, and want to setup a proper sitemap using gatsby-plugin-sitemap, however i am strugling to understand what the following line of code does so i can try and customize de code to do what I need, which is integrate the pages and blog posts on the sitemap, and adding a proper lastmod when applicable. I am breaking my head but cannot get the last part to work, that is, adding lastmod when it is a blog post.
on gatsby-config.js:
{
resolve: `gatsby-plugin-sitemap`,
options: {
// Exclude specific pages or groups of pages using glob parameters
// See: https://github.com/isaacs/minimatch
// The example below will exclude the single `path/to/page` and all routes beginning with `category`
output: '/',
excludes: [`/legal/*`,`/gracias`],
query: `
{
site {
siteMetadata {
siteUrl
}
buildTime(formatString: "YYYY-MM-DD")
}
allSitePage {
nodes {
path
}
}
allMarkdownRemark(filter: {frontmatter: {path: {regex: "/blog/"}}}) {
nodes {
frontmatter {
path
date
}
}
}
}
`,
resolvePages: ({
allSitePage: { nodes: allPages },
allMarkdownRemark: { nodes: allPosts },
}) => {
const pathToDateMap = {};
allPosts.map(post => {
pathToDateMap [post.frontmatter.path] = { date: post.frontmatter.date };
});
const pages = allPages.map(page => {
return { ...page, ...pathToDateMap [page.path] };//what does this line of code do???
});
return pages;
},
serialize: ({ path, date, buildTime }) => {
let entry = {
url: path,
changefreq: 'monthly',
priority: 0.5,
};
if (date) {
entry.priority = 0.7;
entry.lastmod = date;
} else {
entry.lastmod = buildtime;
}
return entry;
}
}
}
For your knowledge both develop and build are succesful, the sitemap is generated as sitemap-index and sitemap-0.xml, and the output is there but no page has lastmod on it.
Thank you all for your help,
With this:
return { ...page, ...pathToDateMap [page.path] };
You are merging objects using the spread operator (...). So, you are returning an object that is the result of all properties of the page and pathToDateMap(at page.path position).
For example:
let person = {
firstName: 'John',
lastName: 'Doe',
age: 25,
};
let job = {
jobTitle: 'Web developer',
location: 'USA'
};
let employee = {
...person,
...job
};
console.log(employee)
In the snippet above, employee is the result of merging person and job.
The lastmod parameter is not added by default unless your source has it (WordPress normally has) but you can follow this answer https://stackoverflow.com/a/70297982/5585371 to create your own.

how should i make my database fetch continously

function shield() {
setInterval(async function () {
const ProfileModelS = require("../models/ProfileSchema");
await ProfileModelS.find({}).then((doc) => {
doc.forEach(async (u) => {
if (u.ShieldPoints <= 0) return console.log(u.Name);
if (u.ShieldPoints > 0) {
await ProfileModelS.findOneAndUpdate(
{ userID: u.userID },
{
$inc: {
ShieldPoints: -1,
},
},
console.log("done")
);
}
});
});
}, 1000);
}
module.exports = shield
I want my mongodb to fetch the model on every Interval but its not doing that, whenever i run my code it fetches the model for example, it will fetch
[{name: 'Joseph' , Points: 10}, {name: 'carman' , Points: -1}, {name: 'thee' , Points: 2}]
according to code it will properly not reduce the points of objects whose points are less than 0,
but it will go on decreasing points of object more than 0, i want it to stop reducing points if the objects point reaches 0, and it should go on decreasing the points of object whose points are greater than 0
In short the process for a particular object should be stop once it points reaches 0
You could try using Promise.all to iterate over all of the retrieved models, as you seem to want to perform an async operation on every instance with ShieldPoints > 0 and your findOneAndUpdate operations are independent of one another.
function shield() {
// Import the model schema
const ProfileModelS = require("../models/ProfileSchema");
setInterval(async function () {
// Retrieve all models
const users = await ProfileModelS.find();
// Parallelise the process of updating the models that need to be updated
Promise.all(
users.map(async (user) => {
if (user.ShieldPoints > 0) {
await ProfileModelS.findOneAndUpdate(
{ userID: user.userID },
{
$inc: {
ShieldPoints: -1,
},
},
);
};
})
);
}, 1000);
}
module.exports = shield
However, if your findOneAndUpdate operations do not complete within the 1000ms interval, then the code will issue a second findOneAndUpdate operation for those same model instances. This could then cause your models to update more than once, which is unintended behaviour. To address this, you would need to add some form of guard against this.

Using Axios with react-chrono

I am not able to get react chrono working when I tried to use the state data. I did a console log on conversationData and is it did print out and as well as conversationtracking. I did get an error that says: "Expected an assignment or function call and instead saw an expression". I am wondering where did I go wrong? here is the code of what I am trying to do:
const data = [
conversationData.map(record => record.conversationtracking.map(items => {
{
{
title: moment(items.conversationdate).format('MMMM DD, YYYY')
cardTitle: items._id
cardSubtitle: items.name
cardDetailedText: items.text
}
}
}))
];
return (
<div>
<h1>sdvsdv</h1>
<Chrono
items={data}
mode="VERTICAL"
/>
</div>
)
You needed return the data after map. Also you were missing , in your object defined inside the second map loop.
PS: The way you are trying to double map the objects will impact the performance. You may need to find a better way to save that extra loop.
// As per the map structure, I hope the data is like this
var conversationData = [{
conversationtracking: [{
_id: 1,
name: 'AVC0',
text: 'RESDS',
conversationdate: '23/12/2020'
}]
}]
const data = [
conversationData.map(record => record.conversationtracking.map(items => {
return {
title: items.conversationdate,
cardTitle: items._id,
cardSubtitle: items.name,
cardDetailedText: items.text
}
}))
];
console.log(data)

Return specific array from object collection

So I get some data into my socket
The code in Client is :
useEffect(() => {
const socket = io("http://localhost:5000/api/socket");
socket.on("newThought", (thought) => {
console.log(thought);
});
}, []);
And then the code in my server is
connection.once("open", () => {
console.log("MongoDB database connected");
console.log("Setting change streams");
const thoughtChangeStream = connection.collection("phonenumbers").watch();
thoughtChangeStream.on("change", (change) => {
io.of("/api/socket").emit("newThought", change);
});
});
When something in my "phonenumbers" collection gets changed I get in return the whole collection . How would I be able to only get the array that got changed from the object in collection?
So for example if in the collection the only service that changed is the one with id "607deefd13c4ebcbcfa0900a" that should be the only one returned and not the whole collection object.
The fullDocument parameter to the options (second) argument to the watch method can be used to get a delta describing the changes to the document for update operations:
const thoughtChangeStream = connection.collection("phonenumbers").watch([], {
fullDocument: 'updateLookup'
});
thoughtChangeStream.on("change", (change) => {
io.of("/api/socket").emit("newThought", change);
});
This will then return a response document like this where updateDescription contains the fields that were modified by the update:
{
_id: {
_data: '8260931772000000012B022C0100296E5A1004ABFC09CB5798444C8126B1DBABB9859946645F696400646082EA7F05B619F0D586DA440004'
},
operationType: 'update',
clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 1, high_: 1620252530 },
ns: { db: 'yourDatabase', coll: 'yourCollection' },
documentKey: { _id: 6082ea7f05b619f0d586da44 },
updateDescription: {
updatedFields: { updatedField: 'newValue' },
removedFields: []
}
}
Note: This will only work for update operations and will not work for replace, delete, insert, etc.
See also:
http://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html.
https://docs.mongodb.com/manual/reference/change-events/

Count the duplicates in a string array using React JS

Following is a code I implemented to create a bar chart using chart js in React app. Here it creates a bar chart with all the data in an array. But, I want to change this code only to give the output in the x-axis - destination, y-axis - no. of occurrence of this destination since it has many repeated destinations.
I searched methods to this but I couldn't get a correct solution.
Can anyone help me to do this?
const dataArrayY4 = [];
res.data.map(item => {
dataArrayY4.push(item.time)
})
const dataArrayX4 = []
res.data.map(item => {
dataArrayX4.push(item.destination)
})
this.setState({
data4: dataArrayY4,
labels4: dataArrayX4,
});
This could be done as follows:
const res = {
data: [
{ time: 1, destination: 'A'},
{ time: 3, destination: 'A'},
{ time: 2, destination: 'B'}
]
};
let tmp4 = [];
res.data.map((o, i) => {
const existing = tmp4.find(e => e.destination == o.destination);
if (existing) {
existing.time += o.time;
} else {
tmp4.push({time: o.time, destination: o.destination});
}
})
this.setState({
data4: tmp.map(o => o.time);
labels4: tmp.map(o => o.destination);
});
Above code could further be optimized by using Array.reduce() instead of Array.map().
I would make the code more efficient. Instead of dataArrayY4 being an array, I would make it an object that has a key of value and the number of occurrence of each value. This way, you can count all the number of occurrences of the all items in res.data
const dataArrayY4 = {};
res.data.map(item => {
dataArrayY4[item.destination] = (dataArrayY4[item.destination] || 0) + 1
})
const dataArrayX4 = []
res.data.forEach(item => {
dataArrayX4.push(item.destination)
})
this.setState({
data4: dataArrayY4,
labels4: dataArrayX4,
});
Then if you want to look for the occurrence of a particular value you
use this eg. Sri Lanka
this.state.data4['Sri Lanka']

Resources