In my app I have a mutation where I want to use the optimisticUpdater to update the view before the server response. For that I need to remove some records from a list into the relay store, like that:
optimisticUpdater: storeProxy => {
storeProxy.delete(recordDataID)
}
The problem is that relay don't remove the record, but it transforms the record in null.
This can be annoying because I have to filter the list every time I use it in my app.
Some one know how can I remove the record ?
thx
You have to remove your records from the list
optimisticUpdater: (store) => {
const listOfRecords = store.getRoot().getLinkedRecords('list')
const newList = listOfRecords.filter(record => record.getDataID() !== recordDataID)
store.getRoot().setLinkedRecords(newList, 'list')
}
In this example I assume your list is placed at the root of your graph
If you decorate your query with #connection, then you can use the ConnectionHandler to easily remove records:
const query = graphql`query WidgetListQuery {
widgets(first: 10) #connection(key: "WidgetList_widgets") {
...
}
}`
optimisticUpdater(store) {
const widgets = ConnectionHandler.getConnection(store.getRoot(), 'WidgetList_widgets');
ConnectionHandler.deleteNode(widgets, deleted_widget.id)
}
Related
I want to ask , how to keep save the id's of the check boxes in a state , and whenever i switched back to first page it automatically search the element with id and mark check boxes automatically.
and if i unmark the checkbox , it deletes the id from the state.
i am able to think about the logic , but cant able to code,it
Small help ,will leads to solve this problem
While switching to other pages, i am succesfully saving the data ,by updating the state
`
// push all the unique objects (combination of previous state of selectedPayments and data from list)
setSelectedPayments((prevState) => {
var arr = [...prevState, ...list];
var newState = [
...new Map(arr.map((item) => [item.id, item])).values(),
];
return newState;
});
console.log('Selected payments are', selectedPayments);
`
Also , removing the objects , if again the checkbox is unchecked ,and updating the state
`
// pull all the objects , which got unChecked
setSelectedPayments((prevState) => {
var newState = prevState.filter(function (objFromA) {
return !list.find(function (objFromB) {
return objFromA.id === objFromB.id;
});
});
return newState;
});
`
Only facing issue with keeping track of the checked boxes, i have implimented this, this is keeping track of main(parent checkbox).
How to extract the ids saved and check the checkboxes when we naviagete from one page to another
`
let elementId = e.target.id;
if (selectedBoxes.includes(elementId)) {
const newArray = selectedBoxes.filter((e) => e !== elementId);
setSelectedBoxes(newArray);
} else {
setSelectedBoxes((prevState) => {
return [...prevState, elementId];
});
}
`
First i modified the Res Json , so that it set's a property isSelected = true,
by comparing the element from the selectedPayments
inAll check handler , i set the selectedPayments like this
And render using this
This is how ,i solved this problem.
** Better and improved answers are always welcome, please share your views.
I have two collections: users_unprocessed and users_processed. When a user is new, he will be added to the users_unprocessed collection. If he is processed, he will be deleted and added to the users_processed.
I want to create a list with all users. Therefore I need to find a user in users_processed or users_unprocessed. The list should be reactively and show live updates, therefor I need to use .onSnapshot().
database.collection("users_unprocessed").doc(id).onSnapshot((snapshot) => {
if (snapshot.numChildren > 0) {
setFetchedUser(snapshot.data());
} else {
database.collection('users_unprocessed').doc(id).onSnapshot((snap) => {
if (snapshot.numChildren > 0) {
assignUser(snap)
} else {
// Error Handling
}
})
}
This code is not giving my any result no matter of the doc exists in the users_unprocessed or users_processed.
If both collections are correctly set you just need to use the forEach function on each of them and put them on an array or list or whatever you want. Something like that:
const allUsers = [];
database.collection("users_unprocessed").onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {
allUsers.push(doc.data().name);
});
});
And then you can do the same with the other collection in the same list. If you don't want to put them on an Array but you have any other method or function you just need to change the last part.
I'm trying to use react-query useInfiniteScroll with a basic API, such as the cocktaildb or pokeapi.
useInfiniteQuery takes two parameters: a unique key for the cache and a function it has to run.
It returns a data object, and also a fetchMore function. If fetchMore is called - through an intersection observer for exemple -, useInfiniteQuery call its parameter function again, but with an updated payload thanks to a native callback getFetchMore().
In the official documentation, getFetchMore automatically takes two argument: the last value returned, and all the values returned.
Based on this, their demo takes the value of the previous page number sent by getFetchMore, and performs a new call with an updated page number.
But how can I perform the same kind of thing with a basic api that only return a json?
Here is the official demo code:
function Projects() {
const fetchProjects = (key, cursor = 0) =>
fetch('/api/projects?cursor=' + cursor)
const {
status,
data,
isFetching,
isFetchingMore,
fetchMore,
canFetchMore,
} = useInfiniteQuery('projects', fetchProjects, {
getFetchMore: (lastGroup, allGroups) => lastGroup.nextCursor,
})
infinite scrolling relies on pagination, so to utilize this component, you'd need to somehow track what page you are on, and if there are more pages. If you're working with a list of elements, you could check to see if less elements where returned in your last query. For example, if you get 5 new items on each new fetch, and on the last fetch you got only 4, you've probably reached the edge of the list.
so in that case you'd check if lastGroup.length < 5, and if that returns true, return false (stop fetching more pages).
In case there are more pages to fetch, you'd need to return the number of the current page from getFetchMore, so that the query uses it as a parameter. One way of measuring what page you might be on would be to count how many array exist inside the data object, since infiniteQuery places each new page into a separate array inside data. so if the length of data array is 1, it means you have fetched only page 1, in which case you'd want to return the number 2.
final result:
getFetchMore: (lastGroup, allGroups) => {
const morePagesExist = lastGroup?.length === 5
if (!morePagesExist) return false;
return allGroups.length+1
}
now you just need to use getMore to fetch more pages.
The steps are:
Waiting for useInfiniteQuery to request the first group of data by default.
Returning the information for the next query in getNextPageParam.
Calling fetchNextPage function.
Reference https://react-query.tanstack.com/guides/infinite-queries
Example 1 with rest api
const fetchProjects = ({ pageParam = 0 }) =>
fetch('/api/projects?cursor=' + pageParam)
const {
data,
isLoading,
fetchNextPage,
hasNextPage,
} = useInfiniteQuery('projects', fetchProjects, {
getNextPageParam: (lastPage) => {
// lastPage signature depends on your api respond, below is a pseudocode
if (lastPage.hasNextPage) {
return lastPage.nextCursor;
}
return undefined;
},
})
Example 2 with graphql query (pseudocode)
const {
data,
fetchNextPage,
isLoading,
} = useInfiniteQuery(
['GetProjectsKeyQuery'],
async ({ pageParam }) => {
return graphqlClient.request(GetProjectsQuery, {
isPublic: true, // some condition/variables if you have
first: NBR_OF_ELEMENTS_TO_FETCH, // 10 to start with
cursor: pageParam,
});
},
{
getNextPageParam: (lastPage) => {
// pseudocode, lastPage depends on your api respond
if (lastPage.projects.pageInfo.hasNextPage) {
return lastPage.projects.pageInfo.endCursor;
}
return undefined;
},
},
);
react-query will create data which contains an array called pages. Every time you call api with the new cursor/page/offset it will add new page to pages. You can flatMap data, e.g:
const projects = data.pages.flatMap((p) => p.projects.nodes)
Call fetchNextPage somewhere in your code when you want to call api again for next batch, e.g:
const handleEndReached = () => {
fetchNextPage();
};
Graphql example query:
add to your query after: cursor:
query GetProjectsQuery($isPublic: Boolean, $first: Int, $cursor: Cursor) {
projects(
condition: {isPublic: $isPublic}
first: $first
after: $cursor
) ...
createNewList = (id, input) => {
const foundCard = {...this.state.cards.find(card => id === card.id)};
this.setState(foundCard.list = [...foundCard.list, input]);
};
Hello everyone
There is an array of data (list), which is stored in the state for each object (card).
Problem: I can’t seem to add a new element to this array.
The way it is set up creates only one element and change it every time. But I need to create a new one every time. I tried to create a separate array, add to it using newArray.push (input) and then do this.setState (foundCard.list = [... foundCard.list, ... newArray])`, but have the same result.
I don’t use Redux, because I just started to learn React and I store everything in state yet.
Thanks in advance for your reply.
createNewList = (id, input) => {
const newCards = this.state.cards.map(card => {
if (card.id === id) card.list = [...card.list, input];
return card;
});
this.setState({
cards: newCards
});
};
I am using protractor for e2e testing.
There is a ag-grid table where multiple columns are sorted in ascending order.
How do i go about verifying this?
Picture of Sample table
In AgGrid the rows might not always be displayed in the same order as you insert them from your data-model. But they always get assigned the attribute "row-index" correctly, so you can query for it to identify which rows are displayed at which index.
So in your E2E-tests, you should create two so-called "page objects" (a selector fo something in your view, separated from the text-execution code, google for page object pattern) like this:
// list page object
export class AgGridList {
constructor(private el: ElementFinder) {}
async getAllDisplayedRows(): Promise<AgGridRow[]> {
const rows = $('div.ag-body-container').$$('div.ag-row');
await browser.wait(EC.presenceOf(rows.get(0)), 5000);
const result = await rows.reduce((acc: AgGridRow[], elem) => [...acc, new AgGridArtikelRow(elem)], []);
return await this.sortedRows(result);
}
private async sortedRows(rows: AgGridRow[]): Promise<AgGridRow[]> {
const rowsWithRowsId = [];
for (const row of rows) {
const rowIndex = await row.getRowIndex();
rowsWithRowsId.push({rowIndex, row});
}
rowsWithRowsId.sort((e1, e2) => e1.rowIndex - e2.rowIndex);
return rowsWithRowsId.map(elem => elem.row);
}
}
// row page object
export class AgGridRow {
constructor(private el: ElementFinder) {}
async getRowIndex(): Promise<number> {
const rowIndexAsString: string = await this.el.getAttribute('row-index');
return parseInt(rowIndexAsString, 10);
}
}
And in your test:
it('should display rows in right order', async () => {
const rows = await gridList.getCurrentDisplayedRows(); // gridList is your AgGridList page object, initialised in beforeEach()
// now you can compare the displayed order to the order inside your data model
});
What this code does: you make page objects for accessing the table as a whole and for accessing elements within a row. To accessing the list in the same order as it is displayed in the view, you have to get all displayed rows (with lazy loading or pagination it should be below 100, otherwise your implementation is bad), get the rowIndex from each of them, sort by it and only then return the grid-list to the test-execution (.spec) file.