React-Query: how can I update cache pagination - reactjs

I'm using Paginated for showing data and the user can remove the item. user after a click on the button remove send request delete and get response success.
I want to remove the item in catch react-query.
I don't want to use method refetch
get all items on the server :
const useGetAll = () =>
useQuery(['applications/getAll', page], () => axios.get<GetAllApplication>('localhost:...', { params: { page } }), {
keepPreviousData: true,
})
interface response data
interface GetAllApplication {
hasError: boolean
data: {
meta: {
itemsPerPage: number
totalItems: number
currentPage: number
totalPages: number
sortBy: [['id', 'DESC']]
}
response: {
id: number
name: string
status: 'enable' | 'disable'
}[]
}
}
remove item request with useMutation :
const useRemoveApplication = () =>
useMutation('applications/remove', removeApplication, {
onSuccess({ message },id ) {
toast(message, { type: 'success' })
},
})

You should use queryClient's method setQueryData in your onSuccess.
Reference: react-query docs

Related

The PaymentIntent requires a payment method — React, Django Rest

I have a React app and a Django Rest API.
My goal is to get the PaymentRequestButtonElement working.
In my Stripe dashboard (test mode) I get the following logs:
200 OK
POST
/v1/payment_intents
12:22:55 PM
200 OK
POST
/v1/payment_methods
12:22:54 PM
200 OK
POST
/v1/tokens
12:22:53 PM
But in the Payments tab, I get the following:
The PaymentIntent requires a payment method
Here is my React component:
import React, { useState, useEffect } from 'react';
// import { useNavigate } from 'react-router-dom';
// import { useShoppingCart } from 'use-shopping-cart';
import {
PaymentRequestButtonElement,
useStripe,
} from '#stripe/react-stripe-js';
const PaymentRequest = () => {
// const history = useNavigate();
// const { totalPrice, cartDetails, cartCount } = useShoppingCart();
const stripe = useStripe();
const [paymentRequest, setPaymentRequest] = useState(null);
const price = 350;
const handleButtonClicked = (event) => {
// if (!cartCount) {
// event.preventDefault();
// alert('Cart is empty!');
// return;
// }
paymentRequest.on('paymentmethod', handlePaymentMethodReceived);
paymentRequest.on('cancel', () => {
paymentRequest.off('paymentmethod');
});
return;
};
const handlePaymentMethodReceived = async (event) => {
// Send the cart details and payment details to our function.
const paymentDetails = {
payment_method: event.paymentMethod.id,
shipping: {
name: event.shippingAddress.recipient,
phone: event.shippingAddress.phone,
address: {
line1: event.shippingAddress.addressLine[0],
city: event.shippingAddress.city,
postal_code: event.shippingAddress.postalCode,
state: event.shippingAddress.region,
country: event.shippingAddress.country,
},
},
};
const response = await fetch('https://my-api/create-payment-intent/', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
// cartDetails,
paymentDetails,
amount: price,
currency: 'usd',
payment_method: 'card'
// automatic_payment_methods: true,
}),
}).then((res) => {
return res.json();
});
if (response.error) {
// Report to the browser that the payment failed.
console.log(response.error);
event.complete('fail');
} else {
// Report to the browser that the confirmation was successful, prompting
// it to close the browser payment method collection interface.
event.complete('success');
// Let Stripe.js handle the rest of the payment flow, including 3D Secure if needed.
const { error, paymentIntent } = await stripe.confirmCardPayment(
response.paymentIntent.client_secret
);
if (error) {
console.log(error);
return;
}
if (paymentIntent.status === 'succeeded') {
console.log('Payment succeeded!');
} else {
console.warn(
`Unexpected status: ${paymentIntent.status} for ${paymentIntent}`
);
}
}
};
useEffect(() => {
if (stripe && paymentRequest === null) {
const pr = stripe.paymentRequest({
country: 'US',
currency: 'usd',
total: {
label: 'Demo total',
//
amount: price,
pending: true,
},
requestPayerName: true,
requestPayerEmail: true,
requestShipping: true,
shippingOptions: [
{
id: 'standard-global',
label: 'Global shipping',
detail: 'Handling and delivery fee',
amount: 350,
},
],
});
// Check the availability of the Payment Request API first.
pr.canMakePayment().then((result) => {
if (result) {
setPaymentRequest(pr);
}
});
}
}, [stripe,
paymentRequest,
// totalPrice
]);
useEffect(() => {
if (paymentRequest) {
paymentRequest.update({
total: {
label: 'Demo total',
amount: 350,
pending: false,
},
});
}
}, [
// totalPrice,
paymentRequest
]);
if (paymentRequest) {
return (
<div className="payment-request-button">
<PaymentRequestButtonElement
options={{ paymentRequest }}
onClick={handleButtonClicked}
/>
--- OR ---
</div>
);
}
return '';
};
export default PaymentRequest;
and here is my Django REST View
class PaymentIntentView(APIView):
def post(self, request, *args, **kwargs):
amount = request.data.get('amount')
currency = request.data.get('currency')
# automatic_payment_methods = request.data.get('automatic_payment_methods')
try:
intent = stripe.PaymentIntent.create(
amount=amount,
currency=currency,
# automatic_payment_methods={
# 'enabled': True,
# },
# You can also add other options like capture_method, setup_future_usage, etc.
)
return Response({'client_secret': intent.client_secret, 'id': intent.id})
except Exception as e:
return Response({'error': str(e)})
I've tried variations of passing automatic_payments as true and passing the payment_method as 'card', no joy
There's a couple of options that you can do in order to fix the problem here.
Option 1: Pass the PM in the backend
When you call fetch on https://my-api/create-payment-intent/ you are passing the paymentDetails that you're not using in your stripe.PaymentIntent.create method. For this to work, you need to first deserialize your request to get access to this information since it's nested (e.g. this guide). Then you need to pass payment_method to the stripe.PaymentIntent.create method. In this option you don't have to change anything in your frontend code.
Option 2: Pass the PM in the frontend
When you call stripe.confirmCardPayment you can pass in the payment_method as explained here. In this option you don't have to change anything in your backend code but you can remove the paymentDetails from the request to your backend.

Apollo Client cache does not update

I am using Apollo Server / Client and the cache does not seem to work on update mutations. (Create, Delete). The server gets updated but nothing happens on the front end. I have to reload the page to show the new item / show change of an item.
I followed the Apollo docs and modeled it after their sandbox implementation.
Let me know if you need more of my code, thank you.
Here is my code:
<form
onSubmit={(e) => {
e.preventDefault();
createUser(
{
variables: {
name: input.value,
email: input.value,
password: input.value
}
},
{
update(cache, { data: { createUser } }) {
cache.modify({
fields: {
allUsers(existingUsers = []) {
const newUser = cache.writeFragment({
data: { createUser },
fragment: gql`
fragment NewUser on User {
name
email
}
`
});
return existingUsers.concat(newUser);
}
}
});
}
}
);
}}
>
You need to provide an id property in the writeFragment method. Here's the example on the docs:
client.writeFragment({
id: '5',
fragment: gql`
fragment MyTodo on Todo {
completed
}
`,
data: {
completed: true,
},
});
Also, writeFragment returns void, so you need to use readFragment to get the data you want, or just use the data available in the mutation's result

react-admin: changing a list from store without http requests

I am using react-admin and I need to control directly the store from one resource, in my case, the orders resource.
Everytime I run the GET_LISTit appends the new records in the list from the store, but, I would like to get a new list from the server and discard the old ones. Here`s where I retrieve the records:
dataProvider(GET_LIST, 'orders', {
filter: { updatedAt: filterDate }, // Get date from Filter.
sort: { field: 'updatedAt', order: 'DESC' },
pagination: { page: 1, perPage: 999 },
}).then(response => response.data)
So, I decided to manipulate the store directly and after some digging I saw this answer and this code from the source:
const dataReducer: Reducer<RecordSetWithDate> = (
previousState = initialState,
{ payload, meta }
) => {
if (meta && meta.optimistic) {
if (meta.fetch === UPDATE) {
const updatedRecord = {
...previousState[payload.id],
...payload.data,
};
return addRecords([updatedRecord], previousState);
}
if (meta.fetch === UPDATE_MANY) {
const updatedRecords = payload.ids.map(id => ({
...previousState[id],
...payload.data,
}));
return addRecords(updatedRecords, previousState);
}
if (meta.fetch === DELETE) {
return removeRecords([payload.id], previousState);
}
if (meta.fetch === DELETE_MANY) {
return removeRecords(payload.ids, previousState);
}
}
if (!meta || !meta.fetchResponse || meta.fetchStatus !== FETCH_END) {
return previousState;
}
switch (meta.fetchResponse) {
case GET_LIST:
case GET_MANY:
case GET_MANY_REFERENCE:
return addRecords(payload.data, previousState);
case GET_ONE:
case UPDATE:
case CREATE:
return addRecords([payload.data], previousState);
default:
return previousState;
}
};
So, based on that, I created a custom action to delete the old ids from my list and add the new ones retrieved from the data source:
import {GET_LIST, DELETE_MANY, FETCH_END } from 'react-admin';
export const UPDATE_ORDER_ADMIN = 'UPDATE_ORDER_ADMIN';
export const update_orders_admin = (data, oldIDS) => ({
type: UPDATE_ORDER_ADMIN,
payload: { data, ids: oldIDS },
meta: {
resource: 'orders',
optimistic: true,
fetch: DELETE_MANY,
fetchResponse: GET_LIST,
fetchStatus: FETCH_END,
},
});
And I am using this custom action after retrieve data from the backend:
dataProvider(GET_LIST, 'orders', {
filter: { updatedAt: filterDate }, // Get date from Filter.
sort: { field: 'updatedAt', order: 'DESC' },
pagination: { page: 1, perPage: 999 },
}).then(response => response.data)
.then(data => {
const ids = orders ? Object.keys(orders) : [];
update_orders_admin(data, ids);
this.setState({ isLoading: false })
return null;
});
However, the system is calling the DELETE action from backend, trying to delete the records from the database, while, what I would like is just delete these records from my view.
Any thoughts?
In your custom action you have the fetch set as DELETE_MANY which will do a loop over every id performing DELETE operation. Not sure if you implementation will work, but the current error is about that. You could try to remove the fetch ans see what happens, but I think without it he will not fetch records. If I'm not mistaken RA only adds new ids to data, however if data changed in the meantime I don't think it will replace the changed data for that you need to reimplement the data provider to change the update data behaviour which is similar to what you're trying.

ReactJS/Apollo/Graphql: Block data.refetch w/ #skip until onSubmit is clicked but have refetch still available

After some debugging, I found out that when the page loads, #skip is doing what I want which is blocking the execution of the query on page loading. However, when the submit button is clicked the app crashes.
I do not understand why the start and end are undefined?
variables: {start: undefined, end: undefined, skipFi: true}
If I remove skipF: true from following the app works fine:
export default graphql(getObjQ, {
options: (props) => ({
variables: {
start: props.start,
end: props.end,
skipF: true
}
})
)(Comp);
I am trying to apply the concept from here to my app:
export default graphql(gql`
query SearchQuery($query: String, $first: Int, $skip: Boolean!) {
search(query: $query, first: $first) #skip(if: $skip) {
edges {
....
}
}
}
`, { options: {variables: { skip: true } }})(Input);
The handleSubmit function looks like this:
handleSubmit = event => {
event.preventDefault();
this.setState({
errMsg: "",
start: new Date(document.getElementById("start").value).valueOf(),
end: new Date(document.getElementById("end").value).valueOf(),
submit: true,
}, () => {
this.props.data.refetch({
start: this.state.start,
end:this.state.end,
});
The query looks like this which is in a separate file/component:
const Query =gql`
query getObjQ($start: Float!, $end: Float!, $skipF: Boolean!
)
{
abs(start: $start, end:$end) #skip(if: $skipF)
Too many 'moving parts' - try hardcoded values first:
handleSubmit = event => {
event.preventDefault();
this.props.data.refetch({
start: XXXXXXXXXXX,
end: YYYYYYYYY,
skipF: false
});
}
UPDATE:
method for debugging options parameters (return object):
export default graphql(getObjQ, {
options: (props) => {
console.log(props.start);
return {
variables: {
start: props.start,
end: props.end,
skipF: true
}
}
}
})(Comp);

How to make polling on react-table

I'm trying to build a React-Table which can make polling to a remote server every second to fetch newest data. I just followed what the author did in the doc (https://react-table.js.org/#/story/server-side-data) and tried integrate the polling function (setInterval) in "componentDidMount" but it FAILED.
The error message shows that when running "requestData" under "componentDidMount", "filtered" is undefined, whose length is not accessible. How can I fix that? Thank you.
import React from 'react';
import _ from 'lodash'
import ReactTable from "react-table";
import 'react-table/react-table.css'
const requestData = (pageSize, page, sorted, filtered) => {
return fetch(
'http://127.0.0.1:5000/agent',
{ method: 'GET'}
).then( res => res.json()
).then( filteredData => {
if (filtered.length) {
filteredData = filtered.reduce((filteredSoFar, nextFilter) => {
return filteredSoFar.filter(row => {
return (row[nextFilter.id] + "").includes(nextFilter.value);
});
}, filteredData);
}
const sortedData = _.orderBy(
filteredData,
sorted.map(sort => {
return row => {
if (row[sort.id] === null || row[sort.id] === undefined) {
return -Infinity;
}
return typeof row[sort.id] === "string"
? row[sort.id].toLowerCase()
: row[sort.id];
};
}),
sorted.map(d => (d.desc ? "desc" : "asc"))
);
const res = {
rows: sortedData.slice(pageSize * page, pageSize * page + pageSize),
pages: Math.ceil(filteredData.length / pageSize)
};
return res;
});
};
class AgentTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
pages: null,
// loading: true,
};
this.fetchData = this.fetchData.bind(this);
}
fetchData(state, instance) {
// this.setState({
// loading: true
// });
requestData(
state.pageSize,
state.page,
state.sorted,
state.filtered
).then(res => {
this.setState({
data: res.rows,
pages: res.pages,
// loading: false,
})
})
}
componentDidMount() {
setInterval(
() => requestData(
this.state.pageSize,
this.state.page,
this.state.sorted,
this.state.filtered
).then(res => {
this.setState({
data: res.rows,
pages: res.pages,
// loading: false,
})
}), 5000
);
}
render() {
const { data, pages, loading } = this.state;
return (
<div>
<ReactTable
columns={[
{
Header: "Agent ID",
accessor: "AGENTID"
},
{
Header: "Description",
accessor: "DESCRIPTION"
},
{
Header: "Domain",
accessor: "DOMAIN"
},
{
Header: "Register Time",
accessor: "REGTIME"
},
{
Header: "Status",
accessor: "STATUS"
},
]}
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
data={data}
pages={pages} // Display the total number of pages
loading={loading} // Display the loading overlay when we need it
onFetchData={this.fetchData} // Request new data when things change
filterable
defaultPageSize={20}
className="-striped -highlight"
/>
</div>
);
}
}
export default AgentTable;
First off, you need to understand what the onFetchData callback is.
Taken from the docs, onFetchData is:
This function is called at componentDidMount and any time sorting, pagination or filterting is changed in the table
So what you're trying to achieve here won't work the way tried to.
Seeing as fetching data from a remote server every second isn't one of the conditions that invoke the onFetchData callback, you should try a different approach.
I forked React-Table's Simple Table example and added timed data requests here, this should help you get started.

Resources