I have one query and one subscription, what I am trying to do is add my data to previous query so that it shows the full list.
I have one query which is returning me list of students and I am rendering that on UI like below
function Test(props) {
const { loading, data: dta } = useQuery(GETSTUDENTS);
const { data: d } = useSubscription(GETSUBSTUDENTS, {
onSubscriptionData: ({ subscriptionData: { data } }) => {
let fname = data.getSubStudent.fname;
let lname = data.getSubStudent.lname;
dta.getStudents.push({ fname, lname });
},
});
return (
<div className="">
{dta &&
dta.getStudents.map((li) => {
<div>
<p>{li.fname}</p>
<p>{li.lname}</p>
</div>;
})}
</div>
);
}
export default Test;
But the main issue is the above one is not updating the cache so when I change the routes and come bqack again it takes the previous data only.
So What I wnat to know na what is the best way to do this, I have check subscribeToMore also but did not get idea How to implement that and how it works with hooks.
I am getting some data from subscription and on that basis I want to change some other part so can I use refetchQueries I did not found any good tutorial which uses hooks (react-apollo-hooks) using qraphql
First, you can just use the pooling option of the useQuery instead of subscription,
I suggest you check it.
From Apollo docs:
"In the majority of cases, your client should not use subscriptions to
stay up to date with your backend. Instead, you should poll
intermittently with queries, or re-execute queries on demand when a
user performs a relevant action."
Apollo subscription
If you still want to use the subscription I think you should use the subscribeToMore and to update your cache policy inside the apollo cache file:
const cache = new InMemoryCache({
typePolicies: {
Agenda: {
fields: {
tasks: {
merge(existing = [], incoming: any[]) {
return [...existing, ...incoming];
},
},
},
},
},
});
You can read more about it here: merge cahce
And check that video: youtube apollo cache
Related
This has been beating me up.
I have a vuex store, inside is a folder called "RyansBag" im using to test things.
I have two other folder, Alerts and Inventory. So the folder structure for each of these goes
store> Ryansbag/Alert/...
in my Inventory index.js file, we run a function to add an item to an inventory system.
async addInventory_Catalog({commit}, payload){
try{
const response = await this.$axios.put('Inventory/AddFromCatalogDefault', null, {
params:{
originalUPC: payload.upc,
clientID: payload.clientId,
saleprice: payload.sellPrice,
cost: payload.sellPrice,
Condition: payload.condition.conditionName,
Serial: payload.serialNumber,
Notes: payload.notes,
HoldDays: payload.holdDays,
}
});
console.log(response.data.success)
commit('RyansBag/Alerts/showAlerts', 'You have added a product!', {root: true})
return response.data;
} catch (error) { alert(error); console.log(error); }
},
Here we just pass the item down, and when it's done - commit the changes to our alert which is in store > RyansBag/Alerts.
You can see I tried to call it:
commit('RyansBag/Alerts/showAlerts', 'You have added a product!', {root: true})
My understanding was to simply state the commit is coming from this store as the root state...? But Im not sure if im supposed to register the commit in /Alerts as a global item somehow. ( https://vuex.vuejs.org/guide/modules.html#accessing-global-assets-in-namespaced-modules )
EDIT:::
Edit: There was no commit request in the action. added to post. Now however I get warning to not mutate state outside of state handlers..
Below is the mutation it's requesting to reach inside the alerts.
export const mutations = {
showAlerts(state, message) {
let timeout = 0
if (state.status.showAlert) {
state.status.showAlert = false
timeout = 300
}
setTimeout(() => {
state.status.showAlert = true
state.status.message = message
}, timeout)
},
hideAlerts(state) {
state.status.showAlert = false
},
}
The fix was a) making sure I called commit in the action parameters. and b) not using setTimeout in the mutation, but instead setting a mutation to hide, and using the actions to setTime.
After putting off testing for a while now due to Cypress not allowing visiting chrome:// urls, I decided to finally understand how to unit/integration test my extension - TabMerger. This comes after the many times that I had to manually test the ever growing functionality and in some cases forgot to check a thing or two. Having automated testing will certainly speed up the process and help me be more at peace when adding new functionality.
To do this, I chose Jest since my extension was made with React (CRA). I also used React Testing Library (#testing-library/react) to render all React components for testing.
As I recently made TabMerger open source, the full testing script can be found here
Here is the test case that I want to focus on for this question:
import React from "react";
import { render, fireEvent } from "#testing-library/react";
import * as TabFunc from "../src/Tab/Tab_functions";
import Tab from "../src/Tab/Tab";
var init_groups = {
"group-0": {
color: "#d6ffe0",
created: "11/12/2020 # 22:13:24",
tabs: [
{
title:
"Stack Overflow - Where Developers Learn, Share, & Build Careersaaaaaaaaaaaaaaaaaaaaaa",
url: "https://stackoverflow.com/",
},
{
title: "lichess.org • Free Online Chess",
url: "https://lichess.org/",
},
{
title: "Chess.com - Play Chess Online - Free Games",
url: "https://www.chess.com/",
},
],
title: "Chess",
},
"group-1": {
color: "#c7eeff",
created: "11/12/2020 # 22:15:11",
tabs: [
{
title: "Twitch",
url: "https://www.twitch.tv/",
},
{
title: "reddit: the front page of the internet",
url: "https://www.reddit.com/",
},
],
title: "Social",
},
};
describe("removeTab", () => {
it("correctly adjusts groups and counts when a tab is removed", () => {
var tabs = init_groups["group-0"].tabs;
const { container } = render(<Tab init_tabs={tabs} />);
expect(container.getElementsByClassName("draggable").length).toEqual(3);
var removeTabSpy = jest.spyOn(TabFunc, "removeTab");
fireEvent.click(container.querySelector(".close-tab"));
expect(removeTabSpy).toHaveBeenCalledTimes(1);
expect(container.getElementsByClassName("draggable").length).toEqual(2); // fails (does not remove the tab for some reason)
});
});
I mocked the Chrome API according to my needs, but feel that something is missing. To mock the Chrome API I followed this post (along with many others, even for other test runners like Jasmine): testing chrome.storage.local.set with jest.
Even though the Chrome storage API is mocked, I think the issue lies in this function which gets called upon initial render. That is, I think the chrome.storage.local.get is not actually being executed, but am not sure why.
// ./src/Tab/Tab_functions.js
/**
* Sets the initial tabs based on Chrome's local storage upon initial render.
* If Chrome's local storage is empty, this is set to an empty array.
* #param {function} setTabs For re-rendering the group's tabs
* #param {string} id Used to get the correct group tabs
*/
export function setInitTabs(setTabs, id) {
chrome.storage.local.get("groups", (local) => {
var groups = local.groups;
setTabs((groups && groups[id] && groups[id].tabs) || []);
});
}
The reason I think the mocked Chrome storage API is not working properly is because when I manually set it in my tests, the number of tabs does not increase from 0. Which forced me to pass a prop (props.init_tabs) to my Tab component for testing purposes (https://github.com/lbragile/TabMerger/blob/f78a2694786d11e8270454521f92e679d182b577/src/Tab/Tab.js#L33-L35) - something I want to avoid if possible via setting local storage.
Can someone point me in the right direction? I would like to avoid using libraries like jest-chrome since they abstract too much and make it harder for me to understand what is going on in my tests.
I think I have a solution for this now, so I will share with others.
I made proper mocks for my chrome storage API to use localStorage:
// __mocks__/chromeMock.js
...
storage: {
local: {
...,
get: function (key, cb) {
const item = JSON.parse(localStorage.getItem(key));
cb({ [key]: item });
},
...,
set: function (obj, cb) {
const key = Object.keys(obj)[0];
localStorage.setItem(key, JSON.stringify(obj[key]));
cb();
},
},
...
},
...
Also, to simulate the tab settings on initial render, I have a beforeEach hook which sets my localStorage using the above mock:
// __tests__/Tab.spec.js
var init_ls_entry, init_tabs, mockSet;
beforeEach(() => {
chrome.storage.local.set({ groups: init_groups }, () => {});
init_ls_entry = JSON.parse(localStorage.getItem("groups"));
init_tabs = init_ls_entry["group-0"].tabs;
mockSet = jest.fn(); // mock for setState hooks
});
AND most importantly, when I render(<Tab/>), I noticed that I wasn't supplying the id prop which caused nothing to render (in terms of tabs from localStorage), so now I have this:
// __tests__/Tab.spec.js
describe("removeTab", () => {
it("correctly adjusts storage when a tab is removed", async () => {
const { container } = render(
<Tab id="group-0" setTabTotal={mockSet} setGroups={mockSet} />
);
var removeTabSpy = jest.spyOn(TabFunc, "removeTab");
var chromeSetSpy = jest.spyOn(chrome.storage.local, "set");
fireEvent.click(container.querySelector(".close-tab"));
await waitFor(() => {
expect(chromeSetSpy).toHaveBeenCalled();
});
chrome.storage.local.get("groups", (local) => {
expect(init_tabs.length).toEqual(3);
expect(local.groups["group-0"].tabs.length).toEqual(2);
expect(removeTabSpy).toHaveBeenCalledTimes(1);
});
expect.assertions(4);
});
});
Which passes!!
Now on to drag and drop testing 😊
I am trying create a to fetch data from the server and show them to the user. But the issue is that nothing apart from the dummy information is displayed.
Basically there are two scenarios:
1. Navigating to the page from some other link (This works as expected)
Explanation: Such as going from http://localhost:3000/ to http://localhost:3000/assignments/explore and it renders all the fetched contents as expected.
Vue plugin ss
2. Entering the page directly through the url or press refresh
Explanation: By directly typing http://localhost:3000/assignments/explore in the url nothing is displayed apart from the dummy card
Vue plugin ss
As you can see the length of the assignment state is 1 instead of 3 and the vuex action saveAssignments is also missing in this case
Template tag in explore.vue
...
<div v-for="assignment in assignments" :key="assignment._id">
<Card :assignment="assignment"></Card>
</div>
...
Script Tag in explore.vue
fetch() {
this.$store.dispatch('assignment/fetchAssignment')
},
computed: {
assignments() {
return this.$store.state.assignment.assignments
},
},
assignment.js //Vuex Store
export const state = () => ({
assignments: [ //dummy data
{
_id: '5f1295181ebf00dd0070de1',
title: 'dummy',
Description: 'asfd',
Price: 50,
createdAt: '2020-07-18T06:22:09.037Z',
updatedAt: '2020-07-18T06:22:09.037Z',
__v: 0,
id: '5f12951081ebf00dd0070de1',
},
],
})
export const mutations = {
saveAssignments(state, newAssignments) {
state.assignments = state.assignments.concat(newAssignments)
},
}
export const actions = {
async fetchAssignment({ commit }) {
const data = await this.$axios.$get('assignments')
commit('saveAssignments', data)
},
}
Any help will be appreciated
Found the solution
I just had to add the return statement before the dispatch function in fetch()
fetch(){
return this.$store.dispatch('assignment/fetchAssignment')
}
Found my answer here
Nuxtjs async await in a page doesnt work on page refresh
The reason why I am writing here because I just got a job as a front-end developer and on the first day they threw at me a big project and I lost, very lost, hopeless.
My job would be that I am having a home page where there is a table with some information for example date, hours, the daily wind speed, daily celsius, and under the table there would be information which can be generated from a config panel.
So basically with Vue-router am having two pages, one with the home and one with the config panel, now in the config panel I am having a form where you can write your text you want to display to the home page and my problem is that I have no clue how to display that text from one router to another I tried to make a method where on submitting the form it should somehow commit that message to my Vuex state but when I am about to write it out to the home page it is now showing,
This is what I have in my modules (vuex file) :
const state = {
message: '',
};
const getters = {
message: (state) => {
return state.message;
},
};
const actions = {
setMessage: ({
commit,
state
}, newValue) => {
commit('SET_MESSAGE', newValue);
return state.message;
},
};
const mutations = {
SET_MESSAGE: (state, newValue) => {
state.message = newValue;
console.log(state.message);
},
};
export default {
state,
getters,
actions,
mutations,
};
when I am console logging the state.message the text which got submitted is there now I need to put this text to my homepage, please help me with that I know that this should be an easy one but I cannot solve it.
On home page you should add
created() {
this.$store.dispatch('SET_MESSAGE');
}
This code will call SET_MESSAGE
computed: {
...mapGetters({
message: 'message',
}),
}
After adding this part you can take message value simple call "this.message" in methods or "message" in template
I'm seeking some wisdom from the Angular community. I am working on a simple project using the MEAN stack. I have set up my back-end api and everything is working as expected. Using Postman, I observe expected behavior for both a GET and PUT routes to retrieve/update a single value - a high score - which is saved in it's own document in its own collection in a MongoDB. So far so good.
Where things go off track is when trying to access the PUT api endpoint from within Angular. Accessing the GET endpoint is no problem, and retrieving and displaying data works smoothly. However, after considerable reading and searching, I am stll unable to properly access the PUT endpoint and update the high score data when that event is triggered by gameplay. Below are the snippets of code that I believe to be relevant for reference.
BACK-END CODE:
SCHEMA:
const _scoreSchema = {
name: { type: String, required: true },
value: { type: Number, "default": 0 }
};
ROUTES:
router
.route('/api/score/:highScore')
.put(scoreController.setHighScore);
CONTROLLER:
static setHighScore(req, res) {
scoreDAO
.setHighScore(req.params.highScore)
.then(highScore => res.status(200).json(highScore))
.catch(error => res.status(400).json(error));
}
DAO:
scoreSchema.statics.setHighScore = (value) => {
return new Promise((resolve, reject) => {
score
.findOneAndUpdate(
{"name": "highScore"},
{$set: {"value": value} }
)
.exec(function(err, response) {
err ? reject(err)
: resolve(response);
});
});
}
ANGULAR CODE:
CONTROLLER:
private _updateHighScore(newHighScore): void {
console.log('score to be updated to:', newHighScore)
this._gameService
.updateHighScore(newHighScore);
}
SERVICE:
updateHighScore(newHighScore: Number): Observable<any> {
console.log(newHighScore);
let url = '/api/score/' + newHighScore;
let _scoreStringified = JSON.stringify({value: newHighScore});
let headers = new Headers();
headers.append("Content-Type", "application/json");
return this._http
.put(url , _scoreStringified, {headers})
.map((r) => r.json());
}
Note that the console.log(newHighScore) in the last block of code above correctly prints the value of the new high score to be updated, it's just not being written to the database.
The conceptual question with PUT routes in angular is this: If the api is already set up such that it receives all the information it needs to successfully update the database (via the route param) why is it required to supply all of this information again in the Angular .put() function? It seems like reinventing the wheel and not really utilizing the robust api endpoint that was already created. Said differently, before digging into the docs, I naively was expecting something like .put(url) to be all that was required to call the api, so what is the missing link in my logic?
Thanks!