Paypal Checkout button with React not letting signed in - javascript

I am having issues integrating the Paypal with my react app using sandbox. When I click on the Button, a pop-up of PayPal opens and when I put in my credentials to log in, I get the following error:
I am able to see the sign in form, but it just won't let me sign in and instead I come to see that message.
App.js
import PaypalButton from './PaypalButton';
const CLIENT = {
sandbox: 'xxxxx',
production: 'xxxxx',
};
const ENV = process.env.NODE_ENV === 'production' ? 'production' : 'sandbox';
render() {
const onSuccess = (payment) =>
console.log('Successful payment!', payment);
const onError = (error) =>
console.log('Erroneous payment OR failed to load script!', error);
const onCancel = (data) =>
console.log('Cancelled payment!', data);
return(
<div>
<PaypalButton
client={CLIENT}
env={ENV}
commit={true}
currency={'USD'}
total={500.00}
onSuccess={onSuccess}
onError={onError}
onCancel={onCancel}
/>
</div>
)
}
PaypalButton
import React from 'react';
import ReactDOM from 'react-dom';
import scriptLoader from 'react-async-script-loader';
class PaypalButton extends React.Component {
constructor(props) {
super(props);
this.state = {
showButton: false,
};
window.React = React;
window.ReactDOM = ReactDOM;
}
componentDidMount() {
const {
isScriptLoaded,
isScriptLoadSucceed
} = this.props;
if (isScriptLoaded && isScriptLoadSucceed) {
this.setState({ showButton: true });
}
}
componentWillReceiveProps(nextProps) {
const {
isScriptLoaded,
isScriptLoadSucceed,
} = nextProps;
const isLoadedButWasntLoadedBefore =
!this.state.showButton &&
!this.props.isScriptLoaded &&
isScriptLoaded;
if (isLoadedButWasntLoadedBefore) {
if (isScriptLoadSucceed) {
this.setState({ showButton: true });
}
}
}
render() {
const {
total,
currency,
env,
commit,
client,
onSuccess,
onError,
onCancel,
} = this.props;
const {
showButton,
} = this.state;
const payment = () =>
paypal.rest.payment.create(env, client, {
transactions: [
{
amount: {
total,
currency,
}
},
],
});
const onAuthorize = (data, actions) =>
actions.payment.execute()
.then(() => {
const payment = {
paid: true,
cancelled: false,
payerID: data.payerID,
paymentID: data.paymentID,
paymentToken: data.paymentToken,
returnUrl: data.returnUrl,
};
onSuccess(payment);
});
return (
<div>
{showButton && <paypal.Button.react
env={env}
client={client}
commit={commit}
payment={payment}
onAuthorize={onAuthorize}
onCancel={onCancel}
onError={onError}
/>}
</div>
);
}
}
export default scriptLoader('https://www.paypalobjects.com/api/checkout.js')(PaypalButton);
Can someone please help me solve this issue?

I had the same issue last week. After working for a while, the sandbox started giving me that error. I reverted all my commits to ensure it wasn't an issue with my code. After a day or two, it started to work again.
Seems it was an issue with PayPal's sandbox environment. (Apparently it happens to the best of us).
If you had been sending incorrect data, you would have seen a console.log of the error.

Related

Getting my userName from MS teams with javascript/reactjs

Im trying to get my Teams userPrincipalname out of the context and using it in a fetch URL. Unfortunately its not actually saving my userPrincipalName within {userPrincipalName} but instead it contains: [object Object]
As i can see in the URL its trying to fetch: http://localhost/openims/json.php?function=getDocuments&input=%22[object%20Object]%22
The URL returns the following: {"name":"[object Object]","age":26,"city":"London"}
What am i doing wrong here?
The code:
import React from 'react';
import './App.css';
import * as microsoftTeams from "#microsoft/teams-js";
class Tab extends React.Component {
constructor(props){
super(props)
this.state = {
context: {}
}
}
componentDidMount(){
microsoftTeams.getContext((context, error) => {
this.setState({
context: context
});
});
}
componentDidMount() {
const { userPrincipalName } = this.state.context;
fetch('http://localhost/openims/json.php?function=getDocuments&input='+'"'+ {userPrincipalName} +'"')
.then(res => res.json())
.then((result) => {
this.setState({ ...result });
})
.catch((error) => {
this.setState({ error });
})
.finally(() => {
this.setState({ isLoaded: true })
});
}
render() {
const { error, isLoaded, name, age, city } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
<li>
{name} {age} {city}
</li>
</ul>
);
}
}
}
export default Tab;
The problems I can see are that you need to ensure:
microsoftTeams.getContext takes a callback, so promisify it and then you use then on it (like any promise)
once you've got the context, create a URL dynamically using the value of context.userPrincipalName
the final fetch request (to /openims/json.php endpoint) only happens once all of the above has happened
That should be something like the following (although re-writing your component as functional component would allow you to use React hooks and better handle any cleanup required).
import React from "react";
import "./App.css";
import * as microsoftTeams from "#microsoft/teams-js";
class Tab extends React.Component {
constructor(props) {
super(props);
this.state = { context: {} };
}
componentDidMount() {
new Promise((resolve) => {
microsoftTeams.getContext(resolve);
})
.then((context) => {
this.setState({ context });
const queryParameters = new URLSearchParams({
function: "getDocuments",
input: `"${context.userPrincipalName}"`,
});
console.log(`userPrincipalName is '${context.userPrincipalName}'`);
return fetch(`http://localhost/openims/json.php?${queryParameters}`);
})
.then((res) => res.json())
.then((result) => this.setState({ ...result }))
.catch((error) => this.setState({ error }))
.finally(() => this.setState({ isLoaded: true }));
}
render() {
const { error, isLoaded, name, age, city } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
<li>
{name} {age} {city}
</li>
</ul>
);
}
}
}
export default Tab;
Could you please try with below working code.
import React from 'react';
import * as microsoftTeams from "#microsoft/teams-js";
class Tab extends React.Component {
constructor(props){
super(props)
this.state = {
context: {}
}
}
//React lifecycle method that gets called once a component has finished mounting
//Learn more: https://reactjs.org/docs/react-component.html#componentdidmount
componentDidMount(){
// Get the user context from Teams and set it in the state
microsoftTeams.getContext((context, error) => {
this.setState({
context: context
});
});
// Next steps: Error handling using the error object
}
render() {
let userName = Object.keys(this.state.context).length > 0 ? this.state.context['upn'] : "";
return (
<div>
<h3>Hello World!</h3>
<h1>Congratulations {userName}!</h1> <h3>This is the tab you made :-)</h3>
</div>
);
}
}
export default Tab;

useEffect hook is turning into infinite loop even when the dependency is not changing all the time

Below is my component in reactjs.
import React, { useState, useEffect } from 'react';
import { Link, Redirect } from 'react-router-dom';
import { connect, useDispatch, useSelector } from 'react-redux';
import { loginUserAction } from '../actions/authenticationActions';
import { setCookie } from '../utils/cookies';
const LoginPage = () => {
const [isSuccess, setSuccess] = useState(false);
const [message, setMessage] = useState('');
const dispatch = useDispatch();
const login = useSelector(state => state.login.response);
console.log(login);
useEffect(() => {
if (login !== undefined) {
setSuccess(login.success);
setMessage(login.message);
if (isSuccess) {
setCookie('token', login.token, 1);
}
}
}, [login]);
const onHandleLogin = (event) => {
event.preventDefault();
const email = event.target.email.value;
const password = event.target.password.value;
dispatch(loginUserAction({
email, password,
}));
}
return (
<div>
<h3>Login Page</h3>
{!isSuccess ? <div>{message}</div> : <Redirect to='dashboard' />}
<form onSubmit={onHandleLogin}>
<div>
<label htmlFor="email">Email</label>
<input type="email" name="email" id="email" />
</div>
<div>
<label htmlFor="password">Password</label>
<input type="password" name="password" id="password" />
</div>
<div>
<button>Login</button>
</div>
</form>
Don't have account? <Link to='register'>Register here</Link>
</div>
);
};
export default LoginPage;
It logs user in. As you can see I am using hooks. When I console.log login from useSelector hook, it console's the updated state. Then the useEffect hook gets called. But the problem is the login is not updating all the time. But still useEffect goes into a loop. What am I missing and how can I fix this?
UPDATE
Below is my reducer
import * as types from '../actions';
export default function(state = [], action) {
const response = action.response;
switch(action.type) {
case types.LOGIN_USER_SUCCESS:
return { ...state, response };
case types.LOGIN_USER_ERROR:
return { ...state, response };
default:
return state;
}
};
Here is the action.
import * as types from './index';
export const loginUserAction = (user) => {
return {
type: types.LOGIN_USER,
user
}
};
A possible solution would be to destructure the object to make the comparison easier
const {message = '', success = false, token = ''} = useSelector(state => state.login.response || {}); //should prevent the error, of response is undefined
console.log(message, success);
useEffect(() => {
//there are other condition options like maybe if(message?.length)
if (message) {
setMessage(message);
}
// Can move setSuccess out of the if, to setSuccess even when it is falsy
if (success) { //note that using isSuccess here might not work cause the state might be the old one still
setSuccess(success)
setCookie('token', token, 1);
}
}, [message, success, token]); //having scalar values (string and boolean) will prevent the loop.
if (login && !isSuccess) { // Here
setSuccess(login.success);
setMessage(login.message);
if (isSuccess) {
setCookie('token', login.token, 1);
}
}
Try to add this and see if this works
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
loading: false,
error: null,
user: null,
isUserLogged: false,
};
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
userAuthStart(state, action) {
return {
...state,
loading: true,
error: null,
user: null,
isUserLogged: false,
};
},
userAuthSuccess(state, { payload }) {
return { ...state, loading: false, user: payload, isUserLogged: true };
},
userAuthFail(state, { payload }) {
return { ...state, loading: false, error: payload, isUserLogged: false };
},
userLogout(state) {
return {
...state,
loading: false,
error: null,
user: null,
isUserLogged: false,
};
},
},
});
export const {
userAuthStart,
userAuthSuccess,
userAuthFail,
userLogout,
} = authSlice.actions;
export default authSlice.reducer;
I use #reduxjs/toolkit for redux.
You can declare and update state of isUserLogged or something to true if user is logged successfully. Then, you can use useSelector to use it inside components.
How to use
const { isUserLogged } = useSelector(state => state.auth)

Not able to access session storage while page routing in reactjs

When I am routing my page on sign up submit, then I am submitting the email in sessionStorage.
But while getting the item from session storage I am not able to do so. My page is routing to another page after submission of sign up.
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "../axios/axios.js";
export class SignUpRedirect extends Component {
constructor() {
super();
this.state = {
U_Id: null,
isLoading: true,
};
}
async componentDidMount() {
const response = await axios.get("/Organization");
const idData = response.data;
idData.forEach((item) => {
if (item.Email === sessionStorage.getItem("email")) {
this.setState({ U_Id: item.U_Id, isLoading: false });
}
});
}
componentWillUnmount() {
this.setState = (state, callback) => {
return;
};
}
render() {
return (
<div className="donestatus-container">
You Have Signed Up And Your Organization Id is :{" "}
{this.state.isLoading ? "...loading" : this.state.U_Id}
<div className="donestatus-container-inner">
<Link to="/sign-in">
<h4>Sign In</h4>
</Link>
</div>
</div>
);
}
}
export default SignUpRedirect;
I am not able to find suitable answer. Can anyone help me out?
Try below code, as you need to parse sessionStorage object, which is like a JSON Onject:
async componentDidMount() {
const response = await axios.get("/Organization");
const idData = response.data;
idData.forEach((item) => {
if (item.Email === JSON.parse(sessionStorage.getItem("email"))) {
this.setState({ U_Id: item.U_Id, isLoading: false });
}
});
}

React Native - expo background - unable to access redux store value?

I am building an app with react native and expo background task manager.
I am trying to access my redux store from a functional component and dispatching values, but I keep running into an issue where the value seems to change but then I can't seem to access the true value of the variable. Below is the flow:
Click tracking -> background fires -> person gets checkedin (submitted: true) -> next ping should see the value of true and checkout (submitted: false).
Instead I keep getting a value of false in my task manager file so the code keeps checking me in instead of going back and forth.
Below is my code:
App.js
import React, { Component } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Button, Platform, Alert } from 'react-native';
import * as Location from "expo-location";
import { configureBgTasks } from './task';
import * as TaskManager from 'expo-task-manager';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { connect } from "react-redux";
import ourReducer from './store/reducer';
const store = createStore(ourReducer);
const TASK_FETCH_LOCATION_TEST = 'background-location-task';
class App extends Component {
state = {
submitted: false
}
async componentDidMount() {
const { status } = await Location.requestPermissionsAsync();
if (status === 'granted') {
console.log('location permissions are granted...')
}
}
stopBackgroundUpdate = async () => {
Alert.alert('TRACKING IS STOPPED');
//Location.stopLocationUpdatesAsync(TASK_FETCH_LOCATION_TEST)
//UNREGISTER TASK
//const TASK_FETCH_LOCATION_TEST = 'background-location-task_global';
TaskManager.unregisterTaskAsync(TASK_FETCH_LOCATION_TEST);
}
//REFERENCES TO STATE
autoTrackingCheckin = () => {
console.log('^^firing checkin')
this.setState({ submitted: true });
store.dispatch({ type: "SUBMITTED", value: true })
}
autoTrackingCheckout = () => {
console.log('^^firing checkout')
this.setState({ submitted: false });
store.dispatch({ type: "SUBMITTED", value: false })
}
executeBackground = async () => {
//START LOCATION TRACKING
const startBackgroundUpdate = async () => {
Alert.alert('TRACKING IS STARTED');
if(Platform.OS==='ios') {
await Location.startLocationUpdatesAsync(TASK_FETCH_LOCATION_TEST, {
accuracy: Location.Accuracy.BestForNavigation,
//timeInterval: 1000,
distanceInterval: 2, // minimum change (in meters) betweens updates
//deferredUpdatesInterval: 1000, // minimum interval (in milliseconds) between updates
// foregroundService is how you get the task to be updated as often as would be if the app was open
foregroundService: {
notificationTitle: 'Using your location for TESTING',
notificationBody: 'To turn off, go back to the app and toggle tracking.',
},
pausesUpdatesAutomatically: false,
});
} else {
await Location.startLocationUpdatesAsync(TASK_FETCH_LOCATION_TEST, {
accuracy: Location.Accuracy.BestForNavigation,
timeInterval: 1000,
//distanceInterval: 1, // minimum change (in meters) betweens updates
//deferredUpdatesInterval: 1000, // minimum interval (in milliseconds) between updates
// foregroundService is how you get the task to be updated as often as would be if the app was open
foregroundService: {
notificationTitle: 'Using your location for TESTING',
notificationBody: 'To turn off, go back to the app and toggle tracking.',
},
pausesUpdatesAutomatically: false,
});
}
}
//WHERE THE MAGIC IS SUPPOSED TO HAPPEN
try {
//REFERENCES FOR VARIABLES AND FUNCTIONS
const submitted = this.state.submitted
const autoCheckin = this.autoTrackingCheckin
const autoCheckout = this.autoTrackingCheckout
const reducerSubmitted = store.getState().reducer.submitted
console.log('THE VARIABLE BEING PASSED...',reducerSubmitted)
configureBgTasks({ autoCheckin, autoCheckout })
startBackgroundUpdate();
}
catch (error) {
console.log(error)
}
}
render() {
//console.log('***********APP.JS STATUS:', this.state.submitted);
console.log('***********REDUCER APP.JS STATUS:', store.getState().reducer.submitted);
return (
<Provider store={ store }>
<View style={styles.container}>
<Button
onPress={this.executeBackground}
title="START TRACKING"
/>
<Button
onPress={this.stopBackgroundUpdate}
title="STOP TRACKING"
/>
<StatusBar style="auto" />
</View>
</Provider>
);
}
}
const mapStateToProps = (state) => {
const { reducer } = state
return { reducer }
};
const mapDispachToProps = dispatch => {
return {
storeCheck: (y) => dispatch({ type: "SUBMITTED", value: y })
};
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'space-evenly',
},
});
Reducer.js
import { combineReducers } from 'redux';
const INITIAL_STATE = {
submitted: false
};
const ourReducer = (state = INITIAL_STATE, action) => {
const newState = { ...state };
switch (action.type) {
case "SUBMITTED":
return {
...state,
submitted: action.value
}
break;
case "STORE_USER_ID":
return {
...state,
userId: [action.value, action.value1, action.value2, action.value3],
}
break;
}
return newState;
};
export default combineReducers({
reducer: ourReducer,
});
task.js
import * as TaskManager from 'expo-task-manager';
import { connect } from "react-redux";
const TASK_FETCH_LOCATION_TEST = 'background-location-task';
import ourReducer from './store/reducer';
import { createStore } from 'redux';
const store = createStore(ourReducer);
export const configureBgTasks = ({ autoCheckin, autoCheckout }) => {
TaskManager.defineTask(TASK_FETCH_LOCATION_TEST, ({ data, error }) => {
if (error) {
// Error occurred - check `error.message` for more details.
return;
}
if (data) {
//get location data from background
const { locations } = data;
//let myStatus = store.getState().reducer.submitted
//console.log('****LOCATION PINGING... submitted IS NOW:', submitted);
console.log('****REDUCER LOCATION PINGING... submitted IS NOW:', store.getState().reducer.submitted);
if (store.getState().reducer.submitted === false) {
autoCheckin();
//store.dispatch({ type: "SUBMITTED", value: true })
console.log('****CHECKING YOU IN...');
} else if(store.getState().reducer.submitted === true) {
autoCheckout();
//store.dispatch({ type: "SUBMITTED", value: false })
console.log('*****CHECKING YOU OUT...')
}
}
})
}
const mapStateToProps = (state) => {
const { reducer } = state
return { reducer }
};
const mapDispachToProps = dispatch => {
return {
storeCheck: (y) => dispatch({ type: "SUBMITTED", value: y })
};
};
//export default connect(mapStateToProps, mapDispachToProps)(configureBgTasks);
//export default configureBgTasks;
store.subscribe(configureBgTasks)

React loader won't stay visible during 'pending' axios requests

I'm using react context & axios interceptors to hide/show a loading spinner. While it does hide and show correctly when requests and responses are fired, my loading spinner is only showing at the start of a network request. The request fires and then goes into a 'pending' status. During the 'pending' status, the response interceptor is fired and hides the loading spinner.
How can I make sure the loading spinner stays visible during the pending requests?
I tried adding some console logs to fire when the requests, responses, and errors were returned including the count, and it showed it (for two requests) to successfully go from 0 - 1 - 2 - 1 - 0, but in the chrome devtools network tab showed as pending even though all requests were returned.
EDIT: thought I had it working after some refactor but it was a no-go. Added updated code
import React, { useReducer, useRef, useEffect, useCallback } from "react";
import { api } from "api/api";
import LoadingReducer from "reducer/LoadingReducer";
const LoadingContext = React.createContext();
export const LoadingProvider = ({ children }) => {
const [loader, dispatch] = useReducer(LoadingReducer, {
loading: false,
count: 0,
});
const loaderKeepAlive = useRef(null),
showLoader = useRef(null);
const showLoading = useCallback(() => {
dispatch({
type: "SHOW_LOADING",
});
}, [dispatch]);
const hideLoading = useCallback(() => {
loaderKeepAlive.current = setTimeout(() => {
dispatch({
type: "HIDE_LOADING",
});
}, 3000);
return clearTimeout(loaderKeepAlive.current);
}, [dispatch]);
const requestHandler = useCallback(
(request) => {
dispatch({ type: "SET_COUNT", count: 1 });
return Promise.resolve({ ...request });
},
[dispatch]
);
const errorHandler = useCallback(
(error) => {
dispatch({ type: "SET_COUNT", count: -1 });
return Promise.reject({ ...error });
},
[dispatch]
);
const successHandler = useCallback(
(response) => {
dispatch({ type: "SET_COUNT", count: -1 });
return Promise.resolve({ ...response });
},
[dispatch]
);
useEffect(() => {
if (loader.count === 0) {
hideLoading();
clearTimeout(showLoader.current);
} else {
showLoader.current = setTimeout(() => {
showLoading();
}, 1000);
}
}, [showLoader, showLoading, hideLoading, loader.count]);
useEffect(() => {
if (!api.interceptors.request.handlers[0]) {
api.interceptors.request.use(
(request) => requestHandler(request),
(error) => errorHandler(error)
);
}
if (!api.interceptors.response.handlers[0]) {
api.interceptors.response.use(
(response) => successHandler(response),
(error) => errorHandler(error)
);
}
return () => {
clearTimeout(showLoader.current);
};
}, [errorHandler, requestHandler, successHandler, showLoader]);
return (
<LoadingContext.Provider
value={{
loader,
}}
>
{children}
</LoadingContext.Provider>
);
};
export default LoadingContext;
I think a more standard approach would be to just utilize the loading state to conditionally render the Spinner and the result of the promise to remove it from the DOM (see demo below). Typically, the interceptors are used for returning the error from an API response, since axios defaults to the status error (for example, 404 - not found).
For example, a custom axios interceptor to display an API error:
import get from "lodash.get";
import axios from "axios";
const { baseURL } = process.env;
export const app = axios.create({
baseURL
});
app.interceptors.response.use(
response => response,
error => {
const err = get(error, ["response", "data", "err"]);
return Promise.reject(err || error.message);
}
);
export default app;
Demo
Code
App.js
import React, { useEffect, useCallback, useState } from "react";
import fakeApi from "./api";
import { useAppContext } from "./AppContext";
import Spinner from "./Spinner";
const App = () => {
const { isLoading, error, dispatch } = useAppContext();
const [data, setData] = useState({});
const fetchData = useCallback(async () => {
try {
// this example uses a fake api
// if you want to trigger an error, then pass a status code other than 200
const res = await fakeApi.get(200);
setData(res.data);
dispatch({ type: "loaded" });
} catch (error) {
dispatch({ type: "error", payload: error.toString() });
}
}, [dispatch]);
const reloadData = useCallback(() => {
dispatch({ type: "reset" });
fetchData();
}, [dispatch, fetchData]);
useEffect(() => {
fetchData();
// optionally reset context state on unmount
return () => {
dispatch({ type: "reset" });
};
}, [dispatch, fetchData]);
if (isLoading) return <Spinner />;
if (error) return <p style={{ color: "red" }}>{error}</p>;
return (
<div style={{ textAlign: "center" }}>
<pre
style={{
background: "#ebebeb",
margin: "0 auto 20px",
textAlign: "left",
width: 600
}}
>
<code>{JSON.stringify(data, null, 4)}</code>
</pre>
<button type="button" onClick={reloadData}>
Reload
</button>
</div>
);
};
export default App;
AppContext.js
import React, { createContext, useContext, useReducer } from "react";
const AppContext = createContext();
const initialReducerState = {
isLoading: true,
error: ""
};
const handleLoading = (state, { type, payload }) => {
switch (type) {
case "loaded":
return { isLoading: false, error: "" };
case "error":
return { isLoading: false, error: payload };
case "reset":
return initialReducerState;
default:
return state;
}
};
export const AppContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(handleLoading, initialReducerState);
return (
<AppContext.Provider
value={{
...state,
dispatch
}}
>
{children}
</AppContext.Provider>
);
};
export const useAppContext = () => useContext(AppContext);
export default AppContextProvider;
Spinner.js
import React from "react";
const Spinner = () => <div className="loader">Loading...</div>;
export default Spinner;
fakeApi.js
const data = [{ id: "1", name: "Bob" }];
export const fakeApi = {
get: (status) =>
new Promise((resolve, reject) => {
setTimeout(() => {
status === 200
? resolve({ data })
: reject(new Error("Unable to locate data."));
}, 2000);
})
};
export default fakeApi;
index.js
import React from "react";
import ReactDOM from "react-dom";
import AppContextProvider from "./AppContext";
import App from "./App";
import "./styles.css";
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<AppContextProvider>
<App />
</AppContextProvider>
</React.StrictMode>,
rootElement
);

Categories

Resources