I am integrating google pay in react application. According to documentation, when token is generated it will be pass to gateway for process payment. I am using #google-pay/button-react. How can I pass token to gateway. I didn't find anything in documentation. Is this library send token to gateway by itself?
From google tutorial to send token to gateway
processPayment(paymentDetail) {
const paymentToken = paymentDetail.paymentMethodData.tokenizationData.token;
let paymentData = {
orderId : '12331231232311',
amount : '50.00'
}
axios.post("https://esqa.moneris.com/googlepay/googlepay-api.js", {
body: JSON.stringify({
paymentToken,
paymentData
})
}).then(response => {
if ( response && response.receipt && response.receipt.ResponseCode &&
!isNaN(response.receipt.ResponseCode) )
{
if ( parseInt(response.receipt.ResponseCode) < 50 )
{
alert("Looks like the transaction is approved.");
}
else
{
alert("Looks like the transaction is declined.");
}
}
else
{
throw ("Error processing receipt.");
}
})
}
<GooglePayButton
environment="TEST"
paymentRequest={{
apiVersion: 2,
apiVersionMinor: 0,
allowedPaymentMethods: [
{
type: 'CARD',
parameters: {
allowedAuthMethods: ['PAN_ONLY'],
allowedCardNetworks: ['MASTERCARD', 'VISA', 'DISCOVER', 'AMEX','DISCOVER','JCB','INTERAC'],
},
tokenizationSpecification: {
type: 'PAYMENT_GATEWAY',
parameters: {
gateway: "moneris",
gatewayMerchantId: "monca05217"
},
},
},
],
merchantInfo: {
merchantId: '12345678901234567890',
merchantName: 'Demo Merchant',
},
transactionInfo: {
totalPriceStatus: 'FINAL',
totalPriceLabel: 'Total',
totalPrice: '50.00',
currencyCode: 'USD',
countryCode: 'CA',
},
callbackIntents: ['PAYMENT_AUTHORIZATION'],
emailRequired: true,
}}
onLoadPaymentData={paymentRequest => {
console.log('load payment data', paymentRequest);
this.processPayment(paymentRequest)
}}
onPaymentAuthorized={(paymentData) => ({
transactionState: 'SUCCESS'
})}
onReadyToPayChange={result => {
console.log('ready to pay change', result);
this.setState({isReadyToPay : result.isReadyToPay});
}}
onCancel={() => alert('Cancelled')}
existingPaymentMethodRequired = {true}
/>
You need to call your processPayment method from onLoadPaymentData.
Your processPayment is responsible for calling your backend and passing the payment token to your payment gateway.
Example:
onLoadPaymentData={paymentRequest => {
processPayment();
}}
async function processPayment(paymentData) {
const paymentToken = paymentData.paymentMethodData.tokenizationData.token;
const response = await fetch("/backend/api", {
method: "POST",
body: JSON.stringify({
paymentToken,
orderDetails: {/* details about your order */}
})
});
}
Related
I am implementing Audio/Video call with SIP js and Astrisk server in React JS.I was successful on creating the WebRTC Audio/Video calling. But I am facing an issue with storing the Invitation or Session Object for SIP js. Because Circular JSON data can't be stringed to store.
Assume someone has started calling and the other end got notification of calling and in that case if the page refreshed or reloaded I am unable to recover the call session to take any action(answer/ decline)
/**
* The following code is inside useState and the dependency are handled properly.
* For making it simple and sort I have just copied the required parts. */
const simpleUserDelegate = {
onCallAnswered: (session) => {
console.log(` Call answered`);
if (simpleUser) {
let remoteVideoTrack = simpleUser.getRemoteVideoTrack(session);
if (remoteVideoTrack) {
} else {
setIsAudioCall(true);
}
}
setIsCallAnswered(true);
setIsCallRecieved(false);
localStorage.setItem('isCallRecieved',null);
localStorage.setItem('callerName',null);
localStorage.setItem('callerImage',null);
setIsCallling(false);
},
onCallCreated: (session) => {
setCallSession(session);
console.log(session,` Call created`);
//console.log('session====>',JSON.stringify(session))
// localStorage.setItem('callerUserAgent',JSON.stringify(session._userAgent));
setIsCallling(true);
localStorage.getItem('callerUserAgent')
},
onCallReceived: (invitation) => {
console.log('invitation',invitation);
console.log('invitationSession',invitation.session);
setCallerActiveRoom(invitation._userAgent.options.displayRoomId);
setCallerName(invitation._userAgent.options.displayName);
setCallerImage(invitation._userAgent.options.displayImage);
localStorage.setItem('callerUserAgent',JSON.stringify(invitation.request));
console.log(` Call received`);
// dispatch(setActiveRoomId(invitation._userAgent.options.displayRoomId));
setIsCallRecieved(true);
localStorage.setItem('isCallRecieved',true);
localStorage.setItem('callerName',invitation._userAgent.options.displayName);
localStorage.setItem('callerImage',invitation._userAgent.options.displayImage);
},
onCallHangup: () => {
console.log(` Call hangup`);
setIsCallling(false);
setIsCallRecieved(false);
localStorage.setItem('isCallRecieved',null);
localStorage.setItem('callerName',null);
localStorage.setItem('callerImage',null);
setIsCallAnswered(false);
},
onCallHold: () => {
console.log(` Call hold`);
},
onRegistered: () => {
//console.log('session',session);
console.log(` Call registered`);
},
onUnregistered: () => {
console.log(` Call unregistered`);
},
onServerConnect: () => {
console.log(` server connect`);
},
onServerDisconnect: () => {
console.log(` server dis connect`);
}
};
let simpleUserOptions = {
// traceSip: false,
// logBuiltinEnabled: false,
delegate: simpleUserDelegate,
media: {
constraints: {
audio: true,
video: true
},
local: {
video: document.getElementById('localMedia')
},
remote: {
video: document.getElementById('remoteMedia'),
//audio: remoteAudioRef.current
}
},
userAgentOptions: {
logBuiltinEnabled: true,
logLevel: "debug",
authorizationPassword: password,
authorizationUsername: username,
uri: urI,
noAnswerTimeout : 30,
displayName: name,
displayImage: profileImage,
displayRoomId: `hi${displayRoomId}`
},
};
const simpleUserObj = new Web.SessionManager('wss://pbx.scinner.com:8089/ws', simpleUserOptions);
if(!simpleUserObj.isConnected()){
simpleUserObj
.connect()
.then(() => {
console.log(`${user.username} connected`);
simpleUserObj.register().then(() => {
console.log(`${user.username} registerd`);
}).catch((error) => {
alert("Failed to register.\n" + error);
});
})
.catch((error) => {
alert("Failed to connect.\n" + error);
});
setIsSARegistered(true);
setSimpleUser(simpleUserObj);
setCallerUserAgent
}else{
console.log('isconnected');
setIsSARegistered(true);
}
/**
Set calling
*/
const setCalling = (name, target) => {
simpleUser
.call(target, {
sessionDescriptionHandlerOptions: {
constraints: {
audio: true,
video: true
}
},
inviteWithoutSdp: false
}).then(() => {
console.log(`anon placed a call`);
}).catch((error) => {
console.error(`[${simpleUser.id}] failed to place call`);
console.error(error);
alert("Failed to place call.\n" + error);
});
//setIsCallling(true);
// console.log('isCallling', isCallling)
}
}
const answerCall = () => {
//callSession stored in local state
if (callSession) {
simpleUser.answer(callSession).then(() => {
console.log(`call answered`);
}).catch((error) => {
console.error(`call answered failed`);
console.error(error);
// alert("Failed to place call.\n" + error);
});
}
};
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.
I have a React context I am testing that runs a single function to check for an application update. The checkForUpdate function looks like this:
async function checkForUpdate() {
if (isPlatform('capacitor')) {
const maintanenceURL =
'https://example.com/maintenance.json';
const updateURL =
'https://example.com/update.json';
try {
const maintanenceFetch: AxiosResponse<MaintanenceDataInterface> =
await axios.get(maintanenceURL);
console.log('maintain', maintanenceFetch);
if (maintanenceFetch.data.enabled) {
setUpdateMessage(maintanenceFetch.data.msg);
return;
}
const updateFetch: AxiosResponse<UpdateDataInterface> = await axios.get(
updateURL
);
console.log('updateFetch', updateFetch);
if (updateFetch.data.enabled) {
const capApp = await App.getInfo();
const capAppVersion = capApp.version;
console.log('Thi is a thinkg', capAppVersion);
if (isPlatform('android')) {
console.log('hi');
const { currentAndroid, majorMsg, minorMsg } = updateFetch.data;
const idealVersionArr = currentAndroid.split('.');
const actualVersionArr = capAppVersion.split('.');
if (idealVersionArr[0] !== actualVersionArr[0]) {
setUpdateMessage(majorMsg);
setUpdateAvailable(true);
return;
}
if (idealVersionArr[1] !== actualVersionArr[1]) {
setUpdateMessage(minorMsg);
setUpdateAvailable(true);
return;
}
} else {
const { currentIos, majorMsg, minorMsg } = updateFetch.data;
const idealVersionArr = currentIos.split('.');
const actualVersionArr = capAppVersion.split('.');
if (idealVersionArr[0] !== actualVersionArr[0]) {
setUpdateMessage(majorMsg);
setUpdateAvailable(true);
return;
}
if (idealVersionArr[1] !== actualVersionArr[1]) {
setUpdateMessage(minorMsg);
setUpdateAvailable(true);
return;
}
}
}
} catch (err) {
console.log('Error in checkForUpdate', err);
}
}
}
For some reason, in my test I wrote to test this, my axiosSpy only shows that it has been called 1 time instead of the expected 2 times. The console logs I posted for both get requests run as well. I cannot figure out what I am doing wrong.
Here is the test:
it.only('should render the update page if the fetch call to update bucket is enabled and returns a different major version', async () => {
const isPlatformSpy = jest.spyOn(ionicReact, 'isPlatform');
isPlatformSpy.mockReturnValueOnce(true).mockReturnValueOnce(true);
const appSpy = jest.spyOn(App, 'getInfo');
appSpy.mockResolvedValueOnce({
version: '0.8.0',
name: 'test',
build: '123',
id: 'r132-132',
});
const axiosSpy = jest.spyOn(axios, 'get');
axiosSpy
.mockResolvedValueOnce({
data: {
enabled: false,
msg: {
title: 'App maintenance',
msg: 'We are currently solving an issue where users cannot open the app. This should be solved by end of day 12/31/2022! Thank you for your patience 😁',
btn: 'Ok',
type: 'maintenance',
},
},
})
.mockResolvedValueOnce({
data: {
current: '1.0.0',
currentAndroid: '1.0.0',
currentIos: '2.0.0',
enabled: true,
majorMsg: {
title: 'Important App update',
msg: 'Please update your app to the latest version to continue using it. If you are on iPhone, go to the app store and search MO Gas Tax Back to update your app. The button below does not work but will in the current update!',
btn: 'Download',
type: 'major',
},
minorMsg: {
title: 'App update available',
msg: "There's a new version available, would you like to get it now?",
btn: 'Download',
type: 'minor',
},
},
});
customRender(<UpdateChild />);
expect(axiosSpy).toHaveBeenCalledTimes(2);
});
I'm developing a react calendar app where a user can see their events retrieved from a Google calendar or add their own ones through the app.
I am using #react-oauth/google to sign in and get credentials.
Here I can get a clientId and credential. How can I use them to add/retrieve events to/from a Google Calendar? Should I add credentials to a request object somehow? (NOTE: instead of "" in CLIENT_ID and API_KEY I inserted some valid values that I got here https://console.cloud.google.com/. Authorized JavaScript origins are http://localhost:3000 and http://localhost)
error: {code: 401, data: Array(1), message: 'Request is missing required authentication credent…ogle.com/identity/sign-in/web/devconsole-project.'}
import React from "react";
import { GoogleOAuthProvider, GoogleLogin } from "#react-oauth/google";
const GoogleCalendar = () => {
var gapi = window.gapi;
var CLIENT_ID =
"";
var DISCOVERY_DOCS = [
"https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest",
];
var SCOPES = "https://www.googleapis.com/auth/calendar.events";
var API_KEY = "";
const AddEvent = (credentialResponse) => {
var event = {
summary: "Google I/O 2015",
location: "800 Howard St., San Francisco, CA 94103",
description: "A chance to hear more about Google's developer products.",
start: {
dateTime: "2022-05-21T09:00:00-07:00",
timeZone: "America/Los_Angeles",
},
end: {
dateTime: "2022-05-21T17:00:00-07:00",
timeZone: "America/Los_Angeles",
},
recurrence: ["RRULE:FREQ=DAILY;COUNT=2"],
attendees: [
{ email: "lpage#example.com" },
{ email: "sbrin#example.com" },
],
reminders: {
useDefault: false,
overrides: [
{ method: "email", minutes: 24 * 60 },
{ method: "popup", minutes: 10 },
],
},
};
gapi.client.load("calendar", "v3", () => console.log("bam!"));
var request = gapi.client.calendar.events.insert({
calendarId: "primary",
resource: event,
});
request.execute((event) => {
console.log(event);
window.open(event.htmlLink);
});
};
return (
<>
<GoogleOAuthProvider clientId={CLIENT_ID}>
<GoogleLogin
onSuccess={(credentialResponse) => {
console.log(credentialResponse);
AddEvent(credentialResponse);
}}
onError={() => {
console.log("Login Failed");
}}
/>
</GoogleOAuthProvider>
</>
);
};
export default GoogleCalendar;
In order to add the events, the client should be loaded and signed in before calling add events. What you did is correct but you have to wait for the response of client load then you can call add events:
gapi.client.setApiKey('YOUR_API_KEY');
gapi.client
.load('https://content.googleapis.com/discovery/v1/apis/calendar/v3/rest')
.then((res) => {
var request = gapi.client.calendar.events.insert({
calendarId: 'primary',
resource: event,
});
request.execute((event) => {
console.log(event);
window.open(event.htmlLink);
});
});
you need your access_token to authorize to google APIs, you can get it from using Authorization flows from #react-oauth/google by using useGoogleLogin
i am working on an application that I make API calls to get some users for an id. the API gives you the users in an object of 25 length, and i order to get the other users u have to make other API calls.
I have a parent component from which I look for users and I pass down some variables to my child component:
<UserSection
id={id}
code={code}
open={open}
users={users}
setOpen={setOpen}
handleClose={handleClose}
handleUsers={handleUsers}
total={total}
currentPageNr={currentPageNr}
maxPageNr={maxPageNr}
/>
then in my child component I am using the material ui data grid as follows:
const [rowsState, setRowsState] = React.useState({
page: 0,
pageSize: 25,
rows: [],
loading: false,
});
const rows = [];
useEffect(() => {
let active = true;
(async () => {
setRowsState((prev) => ({ ...prev, loading: true }));
await fetchAllUsers(rowsState.page);
for (let i = 0; i < users.length; i++) {
if (users[i].campaign_id == props.id) {
let row = {
id: i + 1,
col1: i + 1,
col2: users[i].id,
col3: users[i].first_name,
col4: users[i].qualified,
col5: users[i].email,
col6: users[i].source,
col7: users[i].referrer_id,
col8: showName(users[i].referrer_id),
// col9: props.users[i].url,
col10: justSHowReached(users[i].id),
col11: users.filter(
(u) => u.referrer_id == users[i].id && u.qualified
).length,
col12: changeDate(users[i].date),
// col13: "",
};
rows[i] = row;
}
}
const newRows = rows;
console.log("new rows:", newRows);
console.log("eowsState.page:", rowsState.page);
// console.log("===**=== rowsState.pageSize:", rowsState.pageSize);
if (!active) {
return;
}
setRowsState((prev) => ({ ...prev, loading: false, rows: newRows }));
})();
return () => {
active = false;
};
}, [rowsState.page, rowsState.pageSize]);
and this is how I try to fetch users based on page number:
const fetchAllUsers = async (pageNumber) => {
console.log("----------------------------------");
console.log("page number: ", pageNumber);
console.log("----------------------------------");
await fetch(
`........./api/v1/users?page=${pageNumber}`,
{
method: "GET",
headers: new Headers({
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
}),
}
)
.then((res) => res.json())
.then(async (data) => {
// console.log("=================rows=================");
setUsers(data.data);
return data.data;
})
.catch((error) => {
console.log(error);
});
};
so I set my users here which I want to use on data model.
and also
const columns = [
{ field: "col1", headerName: "#", width: 50 },
{ field: "col2", headerName: "Id", width: 100, sortable: false },
{ field: "col3", headerName: "Name", width: 100 },
{ field: "col4", headerName: "Qualified", width: 100 },
{ field: "col5", headerName: "Email", width: 200 },
{ field: "col6", headerName: "Source", width: 75 },
{ field: "col7", headerName: "Referrer Id", width: 125 },
{ field: "col8", headerName: "Referrer Name", width: 125 },
// { field: "col9", headerName: "Url", width: 300 },
{
field: "col10",
headerName: "Reached",
width: 150,
},
{ field: "col11", headerName: "Qualified", width: 150 },
{ field: "col12", headerName: "Date Created", width: 150 },
{
field: "col13",
headerName: "Action",
width: 150,
sortable: false,
filterable: false,
hideable: false,
renderCell: (params) => {
const onClick = (e) => {
e.stopPropagation(); // don't select this row after clicking
const api = params.api;
const thisRow = {};
api
.getAllColumns()
.filter((c) => c.field !== "__check__" && !!c)
.forEach(
(c) => (thisRow[c.field] = params.getValue(params.id, c.field))
);
console.log("---->", thisRow.col2, thisRow.col4);
setUserId(thisRow.col2);
updateUser(thisRow.col2, thisRow.col4);
// return alert(JSON.stringify(thisRow, null, 4));
};
return (
<>
<Button variant="contained" onClick={onClick}>
update
</Button>
</>
);
},
},
];
this is how I make my model:
<DataGrid
// rows={rows}
columns={columns}
pagination
rowCount={props.total}
paginationMode="server"
// pageSize={25}
rowsPerPageOptions={[25]}
{...rowsState}
onPageChange={(page) => {
// console.log("and page is ", page);
setRowsState((prev) => ({ ...prev, page }));
}}
onPageSizeChange={(pageSize) =>
setRowsState((prev) => ({ ...prev, pageSize }))
}
/>
the problem is that I load users but I wont be able to show them inside my model
here u can see:
I am loading 25 users but the model doesn't show anything, however it shows me 1–25 of 5101 when i click on > I can load the users on my model like but now I am on 26–50 of 5101 so I am in page 2 but I am showing the data for page 1, when i click on > again I can see that this works but I am always behinds the correct page and sometimes Im in page 6 but I am still seeing data for page 2, and I can see that model is not being updated correctly.
on my dependency on my useEffect I have [rowsState.page, rowsState.pageSize], while the toturial says I need to have 3, and the 3rd one is the rows, if I put users there, the app will keep rendering and eventually firefox will crush. How can i make sure I am getting the correct data for every page and also how to load the data directly to the model?
There's a lot going on here, and I think your component is overall too complex. If you try and simplify things you might make it easier to see the problem.
First, I'd move the fetchAllUsers out to a separate method - it doesn't need to use state, it can just be a simple wrapper around an API call. Also, given that it's fetching a subset of users, it should probably not be called "fetchAllUsers". And, you're mixing async/await with promises - just stick with using async/await. Something like this might work
const fetchUsersForPage = async (pageNumber) => {
try {
const response = await fetch(
// Copied from your code but looks very suspicious...
`........./api/v1/users?page=${pageNumber}`,
{
method: "GET",
headers: new Headers({
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
}),
});
const { data } = await response.json();
return data;
} catch (error) {
console.log(error);
}
};
I'd also suggest you encapsulate the loading of the paged data into a separate hook. This article does a good job of explaining why you should use custom hooks for encapsulation. Your effect also has a dependency on the props.id which looks like it's a campaign id. Again, something like this might work - there's a few red flags in there which I've commented in the code below:
const usePagedData = (campaignId, page, pageSize) => {
const [loading, setLoading] = useState(false);
const [rows, setRows] = useState([]);
useEffect(() => {
const loadPageData = async () => {
setLoading(true);
const users = await fetchUsersForPage(page);
const userRows = users
// This looks suspicious - you might be losing users because they
// don't match the campaign? Shouldn't you pass the campaignId
// as part of the fetch in that case?
.filter(user => user.campaign_id === campaignId)
.map((user, index) => ({
id: index + 1,
col1: index + 1,
col2: user.id,
col3: user.first_name,
col4: user.qualified,
col5: user.email,
col6: user.source,
col7: user.referrer_id,
// Not sure what these functions are??
col8: showName(user.referrer_id),
// col9: props.users[i].url,
col10: justSHowReached(user.id),
// This number will almost certainly be wrong since 'users' is
// the list of users for this page.
col11: users.filter(u => u.referrer_id == user.id && u.qualified).length,
col12: changeDate(user.date),
// col13: "",
}));
setRows(userRows);
}
loadPageData();
}, [campaignId, page, pageSize]);
return {
rows,
loading
}
}
Now your component that contains the data grid can use your custom hook as follows:
const { rows, loading } = usePagedData(props.id, page, pageSize);