How to send "token" to "Api" in "react" - javascript

I'm a new developer.
If you login to our website, JWT will be created.
When I press the button, I have to put it in the API as a backend.
And if the screen is successful, the address in the API must be printed out.
If it fails, 'authentication failure' should be displayed on the screen.
I want to do this. Please help me.
import axios from 'axios';
import React, { useState } from 'react';
import { Button } from '#material-ui/core';
function TestPage() {
const onLogin = () => {
var variables = {
email: email,
password: password,
};
Axios.post('/auth/login', variables).then((res) => {
setCookie('token', res.payload.accessToken);
setCookie('exp', res.payload.accessTokenExpiresIn);
Axios.defaults.headers.common['Authorization'] = `Bearer ${res.payload.accessToken}`;
Axios.get('/user/me').then((res) => {
console.log(res);
});
});
};
return (
<>
<div>
<Button
variant="contained"
color="primary"
style={{ width: '200px' }}
onClick={(e) => customFetch(e)}>
address
</Button>
</div>
{address && <div>{address}</div>}
</>
);
}
export default TestPage;

Generally for any network operation, it's helpful to know when it's in progress, has finished, and/or has an error. Let's set this up:
const [isLoading, setIsLoading] = useState(false)
const [data, setData] = useState(null)
const [error, setError] = useState(null)
// inside your `onLogin` function...
setIsLoading(true);
Axios.post('/auth/login', variables).then((res) => {
setCookie('token', res.payload.accessToken);
setCookie('exp', res.payload.accessTokenExpiresIn);
Axios.defaults.headers.common['Authorization'] = `Bearer ${res.payload.accessToken}`;
// bit messy using the same error state for both but you can always refactor
Axios.get('/user/me').then((res) => {
console.log(res);
setData(res); // not sure where the actual data is with Axios
}).catch(err => setError(err);
}).catch(err => setError(err));
setIsLoading(false);
During your POST, set the state variables accordingly:
Before the post, setIsLoading(true)
On success, setData(response.data) // whatever your payload might be
On error/failure, setError(error)
Now in your component return, you can conditionally render your different states, e.g:
// your component body
if (isLoading) return (
// a loading state
)
if (error) return (
// an error state
// e.g. "Authentication Failure"
)
return (
// your success/ideal state
// e.g:
<>
<div>
<Button
variant="contained"
color="primary"
style={{ width: '200px' }}
onClick={(e) => customFetch(e)}>
address
</Button>
</div>
{address && <div>{address}</div>}
</>
)
Alternatively, you could leverage the variables slightly differently:
return (
<>
<div>
<Button
variant="contained"
color="primary"
style={{ width: '200px' }}
onClick={(e) => customFetch(e)}
disabled={isLoading}>
address
</Button>
</div>
<div>
{isLoading
? 'Checking...'
: error !== null
? 'Something went wrong'
: 'Ready to submit'}
</div>
</>
)
The ternary style can be a bit messy though.

Related

React UseEffect not updating when information is deleted from array

I'm looking to get some help as I've been stuck on this for a while. I have a MERN stack application interacting with the Github API. Users are able to search for Github users and save them to their profile on the app and once they do that they will also start following the user on Github. In the same fashion users are able to delete the users from their profile in the app and that will unfollow the same user on Github. The problem is that when I click on the delete user button, it does not update on the browser unless I refresh the page then I see that the user is deleted from the array of saved users. In the Profile.jsx component I am rendering the list of saved users and fetching the information from the database. In the SavedGithubUsersCard.jsx component I am mapping through the saved users data to render the user's information such as name. In DeleteButton.jsx I am deleting the specific saved user when I click on the button. My question is what am I doing wrong? is the prop change not being fired or is there another issue with UseEffect?
Here is the code:
import React, { useEffect, useContext } from 'react';
import swal from 'sweetalert';
import { AppContext } from '../context/AppContext';
import SavedGithubUsersCard from './SavedGithubUsersCard';
import axios from 'axios';
Profile.jsx
const Profile = () => {
const {
githubUserData, setGithubUserData
} = useContext(AppContext);
useEffect(() => {
axios.get('/api/githubdata').then((res) => {
setGithubUserData(res.data);
})
.catch((err) => {
if (err) {
swal('Error', 'Something went wrong.', 'error');
}
});
}, [setGithubUserData]);
return (
<div className="profile-saved-users">
<div className="githubUserData-cards">
<h1 className="saved-users-header">Saved users</h1>
{!githubUserData || githubUserData.length === 0 ? (
<p className="no-saved-users-text">No saved users yet :(</p>
) : (
<SavedGithubUsersCard githubUserData={githubUserData}/>
)}
</div>
</div>
);
};
export default Profile;
import React from 'react';
import { Card, Button } from 'react-bootstrap';
import { Link } from 'react-router-dom';
import DeleteButton from './DeleteButton';
SavedGithubUsersCard.jsx
const SavedGithubUsersCard = ({githubUserData}) => {
return (
<>
{githubUserData?.map(githubUserData => (
<Card key={githubUserData._id} id="saved-users-card">
<Card.Img
variant="top"
src={githubUserData.avatar_url}
id="saved-users-card-image"
/>
<Card.Body id="saved-users-card-information">
<Card.Title
id="saved-users-name"
style={{ textAlign: 'center' }}
>
{githubUserData.name}
</Card.Title>
<Card.Subtitle
id="saved-users-username"
className="mb-2 text-muted"
style={{ textAlign: 'center' }}
>
{githubUserData.login}
</Card.Subtitle>
<Card.Text id="saved-users-profile-url">
Profile URL: {githubUserData.html_url}
</Card.Text>
<Link
to={{ pathname: "githubUserData.html_url" }}
target="_blank"
>
<Button
id="saved-users-profile-button"
variant="outline-primary"
>
View profile
</Button>
</Link>
<DeleteButton githubUserDataId={githubUserData._id} githubUserDataLogin= {githubUserData.login} />
</Card.Body>
</Card>
))}
</>
);
};
export default SavedGithubUsersCard
DeleteButton.jsx
import React from 'react';
import { Button } from 'react-bootstrap';
import axios from 'axios';
import swal from 'sweetalert';
const DeleteButton = ({ githubUserDataLogin, githubUserDataId }) => {
const handleRemove = async () => {
try {
fetch(`https://api.github.com/user/following/${githubUserDataLogin}`, {
method: 'DELETE',
headers: {
Authorization: `token ${process.env.GITHUB_TOKEN}`
}
});
await axios({
method: 'DELETE',
url: `/api/githubdata/${githubUserDataId}`,
withCredentials: true
});
swal(
'Removed from profile!',
`You are no longer following ${githubUserDataLogin} on Github`,
'success'
);
} catch (err) {
swal('Error', 'Something went wrong.', 'error');
}
};
return (
<Button
variant="outline-danger"
style={{ marginLeft: 20 }}
onClick={handleRemove}
id="saved-users-delete-button"
>
Remove User
</Button>
);
};
export default DeleteButton;
AppContext.jsx
import React, { createContext, useState, useEffect } from 'react';
import axios from 'axios';
const AppContext = createContext();
const AppContextProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
const [loading, setLoading] = useState(false);
const [githubUserData, setGithubUserData] = useState([]);
const user = sessionStorage.getItem('user');
useEffect(() => {
// incase user refreshes and context is cleared.
if (user && !currentUser) {
axios
.get(`/api/users/me`, {
withCredentials: true
})
.then(({ data }) => {
setCurrentUser(data);
})
.catch((error) => console.error(error));
}
}, [currentUser, user]);
return (
<AppContext.Provider
value={{ currentUser, setCurrentUser, loading, setLoading, githubUserData, setGithubUserData}}
>
{children}
</AppContext.Provider>
);
};
export { AppContext, AppContextProvider };
Thank you!
From what I see of your code when you delete a user you successfully delete them in the back end but don't also update the local state.
Option 1 - Refetch the users list and update local state
const DeleteButton = ({ githubUserDataLogin, githubUserDataId }) => {
const { setGithubUserData } = useContext(AppContext);
const handleRemove = async () => {
try {
fetch(....);
await axios(....);
swal(....);
const usersDataRes = await axios.get('/api/githubdata');
setGithubUserData(usersDataRes.data);
} catch (err) {
swal('Error', 'Something went wrong.', 'error');
}
};
return (
...
);
};
Option 2 - Attempt to manually synchronize the local state
const DeleteButton = ({ githubUserDataLogin, githubUserDataId }) => {
const { setGithubUserData } = useContext(AppContext);
const handleRemove = async () => {
try {
fetch(....);
await axios(....);
swal(....);
setGithubUserData(users =>
users.filter(user => user._id !== githubUserDataId)
);
} catch (err) {
swal('Error', 'Something went wrong.', 'error');
}
};
return (
...
);
};

How to use two contexts for authentication and subscription in Chrome extension?

I want to display the Stripe subscription form after user has signed up into the extension. I want to display home to a subscribed user. And whenever the user opens the extension the home should be displayed if he has already subscribed. If not, it should display subscription form.
But the problem is my app is displaying both the home and subscription form to a subscribed user.
Here is my private route code:
const PrivateRoute = ({ component: RouteComponent, ...rest }) => {
const { currentUser, subscriptionStatus } = useContext(AuthContext);
return (
<Route
{...rest}
render={(routeProps) =>
!!currentUser ? (
!!subscriptionStatus ? (
<RouteComponent {...routeProps} />
)
: (
<Redirect to={"/subscribe"} />
)
) : (
<Redirect to={"/login"} />
)
}
/>
);
};
This is my auth context provider:
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
const [pending, setPending] = useState(true);
const [emailVerified, setEmailVerified] = useState(true);
const [helper, setHelper] = useState(false);
const [subscriptionStatus, setSubscriptionStatus] = useState(null);
useEffect(() => {
app.auth().onAuthStateChanged(async(user) => {
setCurrentUser(user);
if(!user.emailVerified){
setEmailVerified(false);
}else{
setEmailVerified(true);
const fetchData = async () => {
const token = user && (await user.getIdToken());
const payloadHeader = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
};
const status = await fetch('http://localhost:3000/is-subscribed', payloadHeader).then(r => r.json());
if(status == 'active'){
setSubscriptionStatus(status);
setPending(false);
}else{
setPending(false);
}
}
fetchData();
}
});
}, []);
if (pending && helper) {
return <Loader />;
}
if(!emailVerified){
return <>Please verify your email</>
}
return (
<AuthContext.Provider
value={{
currentUser, subscriptionStatus
}}
>
{children}
</AuthContext.Provider>
);
};
Any idea on this?
The easiest option would be redirecting your customers to Checkout to pay, and handling the successful payments in your Firebase app via webhooks, but you can also use the custom flow if you prefer.

How to automatically click submit button - react

I have a react login page with username and password manually inputted,
I want the submit button to automatically click upon page load so it will automatically log in users.
Here is my code.
export const LoginForm = () => {
const history = useHistory();
const classes = useStyles();
const email = 'email#email.com';
const password = 'mypassword';
const [authStatus, setStatus] = useState(null);
const [isSpinnerHidden, setSpinner] = useState(true);
const { session } = useContext(SessionContext);
const recaptchaRef = useRef();
const _onSubmit = async (e) => {
e.preventDefault();
setSpinner(false);
const authResult = await session.signIn(email, password);
setSpinner(true);
console.log(session);
authResult ? setStatus(authResult) : history.push('/dashboard');
};
return (
<form onSubmit={_onSubmit}>
<Typography
align='center'
color='primary'
variant="h4"
style={{ marginBottom: '20px' }}
>
Sign In
</Typography>
{authStatus ?
<Alert
severity="error"
>
{authStatus}
</Alert>
:
null
}
<TextField
type='email'
label="Email"
/>
<TextField
type='password'
label="Password"
/>
<Link
onClick={() => history.push('/restore')}
className={classes.restore}>
Forgot password?
</Link>
<MuiButton
text='Sign In'
type='submit'
value='login'
isSpinnerHidden={isSpinnerHidden}
className={classes.button}
/>
</form>
);
};
what can I add to this code or change that will make it click on the sign-in button automatically and login the user.
In order to automatically click on the submit button, you need to use an onLoad
convert your _onSubmit to a windows onload, something like this
window.onload = async (e) => {
e.preventDefault();
setSpinner(false);
console.log('hello');
const authResult = await session.signIn(email, password);
setSpinner(true);
console.log(session);
authResult ? setStatus(authResult) : history.push('/dashboard');
}
I don't think clicking the button automatically is the best experience you can have here.
The better way of doing that here would be to conditionally choose the route based on authentication state before you even render the login screen. Something like this:
if (isAuthenticated){
history.push('/dashboard);
} else {
history.push('/login');
}
If for whatever reason you NEED to do it your way, just call your signin function when the components mounts with useEffect
import React, {useEffect} from 'react';
export const LoginForm = () => {
...
useEffect(() => {
if(isAuthenticated){ // You'll need to get the auth state from somewhere here
history.push('/dashboard')
}
},[])
return (
...
);
};

expected assignment or function call: no-unused-expressions ReactJS functional component

this is my first react app and I don't understand what's wrong.I'm trying to display the page but I'm getting the error in the title twice(on lines 26:5 - where the fetch(url, { is and 39:9 where I have the line setisLoaded(true), setArticles(result.articles);).I changed the code back and forth from App class component to functional component,but no luck(though fetching the data works/worked).I tried enclosing all the code inside the useEffect() method into a return statement only to have all sort of parsing errors that I can't fix.So here's the code:
import React, { Fragment, useState, useEffect } from 'react';
import Title from './components/Title/Title';
import Content from './components/Content/Content';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Card, Navbar, Form, FormControl, Button } from 'react-bootstrap';
const App = (props) => {
const [error, setError] = useState(null);
const [isLoaded, setisLoaded] = useState(false);
const [articles, setArticles] = useState([]);
const [country, setCountry] = useState('gb');
const handleSubmit = (event) => {
event.preventDefault();
changeCountryHandler();
};
const changeCountryHandler = (event) => {
setCountry(event.target.value);
};
useEffect(() => {
let url =
'https://cors-anywhere.herokuapp.com/http://newsapi.org/v2/top-headlines?country='
+
country;
fetch(url, {
method: 'GET',
headers: {
Accept: 'application/json, text/plain, */*',
'Content-type': 'application/json',
'x-api-key': 'myApiKey',
SameSite: 'None',
},
})
.then((res) => res.json())
.then((result) => {
setisLoaded(true), setArticles(result.articles);
}),
(error) => {
setisLoaded(true);
setError(error);
};
});
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<>
<Navbar className="bg-secondary justify-content-between" variant="dark">
<p>The selected country is:{country}</p>
<Form inline onSubmit={handleSubmit}>
<FormControl
type="text"
placeholder="Search by country"
className=" mr-sm-2"
onChange={changeCountryHandler}
value={country}
/>
<Button type="submit">Submit</Button>
</Form>
</Navbar>
{articles.map((article) => (
<Card
style={{ width: '100%', height: 'auto' }}
key={article.title}
className="mb-5 mt-4 bg-light"
>
<Card.Img
variant="top"
src={article.urlToImage}
className="pr-3 pl-3"
alt=""
/>
<Card.Body>
<Card.Title>
{article.author} | {article.publishedAt}
<Title title={article.title}>{article.title}</Title>
</Card.Title>
Description: {article.description}
<Content content={article.content}>{article.content}</Content>
Read the full article on{' '}
<Card.Link
href={article.url}
target="_blank"
rel="noopener noreferrer"
>
{article.url}
</Card.Link>
</Card.Body>
</Card>
))}
</>
);
}
};
export default App;
Initially I wanted to be able to change the country parameter from the URL in order to fetch the desired content from the API,based on what's submitted from the form(I know I should have a select with options instead of an input type text,but I'm just testing this for now).But at this point,I'm just happy if I'll be able to display any page,since the other responses to this issue(adding a return to the useEffect() arrow function,enclosing statements within parenthesis instead of curly braces) didn't work for me and I don't understand this error.Any idea would be appreciated.
One error is for these:
setisLoaded(true), setArticles(result.articles);
Statements are separated by semicolons, not commas, so the fix is:
setisLoaded(true);
setArticles(result.articles);
One error is a misplaced parentheses, which is causing the error callback to be a separate expression:
}), // <-----
(error) => {
setisLoaded(true);
setError(error);
};
It should instead be:
}, (error) => {
setisLoaded(true);
setError(error);
}); // <----- moved to here
You are missing the dependencies array in the useEffect.
useEffect(() => {
//your code
}, []);
If you add an empty array it will only fetch the data once when your component mounts. If you add any prop or state inside the array it will fetch when the component mounts and also when the prop or state added changes.
Always remember to add the array as a second parameter of the useEffect to avoid that infinite loop.

React: How to show Spinner & Component (at the same time) when State Changes using `useEffect`

I am using conditional Rendering to show the spinner during first API call. And when the date changes as seen in useEffect dependency array, it makes API call again
Question:
If the data fetch is not complete, the old data is shown in the screen. But I want the spinner to show over the old data so that a user can sense that data loading is in progress .
The current implementation i.e conditional rendering only shows one component either the spinner or the Grid.
Spinner
Grid component
import React, { Component, useState, useEffect } from "react";
import { connect } from "react-redux";
import ApiHelper from "../../api/ApiHelper";
import ReactDataGrid from "react-data-grid";
import { Toolbar, Data, Filters } from "react-data-grid-addons";
import JarvisSpinner from "../presentationalComponents/JarvisSpinner";
import { withRouter } from "react-router-dom";
import "../../css/styles.css";
import "../../css/JarvisGrid.css";
const JarvisGrid = (props) => {
const [pagesize, setPageSize] = useState(15);
const [data, setdata] = useState([]);
const [filters, setFilters] = useState({
filterfield: "Application Send",
filtervalue: "Base",
});
const [sort, setSort] = useState({
Field: "PublicOrderNumber",
Direction: "desc",
});
const [loading, setloading] = useState(true);
useEffect(() => {
//Set data
ReloadData();
}, [props.uid, props.CalendarDates.fromDate, props.CalendarDates.toDate]);
// For Loading Data using API
const ReloadData = () => {
ApiHelper.GetGridQueryResult(
props.uid,
props.filterField,
props.filterValue,
props.CalendarDates.fromDate,
props.CalendarDates.toDate
)
.then((response) => {
setdata(response.data.responses);
setloading(!loading);
})
.catch((error) => {
setdata([]);
switch (error.response.status) {
case 403:
console.log("Error code --> " + 403);
props.history.push("/unAuthorizedPage");
break;
default:
console.log("Error String --->" + error);
}
});
};
return (
<>
{loading ? (
<JarvisSpinner size="3x" />
) : (
<div className="JarvisGrid">
<ReactDataGrid
columns={props.columns}
rowGetter={(i) => data[i]}
rowsCount={data.length}
minHeight={500}
uid={props.uid}
enableRowSelect={null}
enableCellSelect
toolbar={<Toolbar enableFilter="true" />}
onAddFilter={(filter) => {
setFilters({
filterfield: filter.column.key,
filtervalue: filter.filterTerm,
});
}}
onClearFilters={() =>
setFilters({
filterfield: "OrderType",
filtervalue: "Base",
})
}
onGridSort={(sortColumn, sortDirection) => {
setSort({ Field: sortColumn, Direction: sortDirection });
}}
/>
</div>
)}
</>
);
};
function mapStateToProps(state) {
return {
CalendarDates: state.calendarDates,
};
}
export default withRouter(connect(mapStateToProps)(JarvisGrid));
Please note that am using redux for state management and react-router.
=================================================
After Implementing the solution provided by MwamiTovi
Now I have to figure out the overlaying stuff.
This is actually fine, based on your logic here:
// Within your "return" statement
return (
<>
{loading ? (
<JarvisSpinner size="3x" /> // renders if `loading === true`
) : (
<div className="JarvisGrid"> // renders if `loading === false`
<ReactDataGrid .../>
</div>
)
)}
</>
)
So this is what you actually want:
First time, load only the spinner during API call
Once data fetch (API call) is a success, show only the Grid component
Upon subsequent API calls, render both the spinner and Grid
Let's this:
// Initiate `data` as `null`
// After the first time, `data` will never be `null` again
const [data, setdata] = useState(null);
// Remember, !null === true since data === null,
// So first time API call, only spinner will show up,
// Upon the next calls, !data === false, so this won't load
if (!data) {
return <JarvisSpinner size="3x" /> ;
}
// Within your "return" statement
// After the first API, all the other API calls will do this...
return (
{loading ? (
<>
<JarvisSpinner size="3x" /> // renders both spinner & component, `loading === true`
<div className="JarvisGrid"> // consider placing an "overlay" onto the component
<ReactDataGrid .../>
</div>
</>
) : (
<div className="JarvisGrid"> // renders only component, when `loading === false`
<ReactDataGrid .../>
</div>
)
)}
)
I would do this in your ReloadData function:
const ReloadData = () => {
setLoading(true); // HERE
ApiHelper.GetGridQueryResult(
props.uid,
props.filterField,
props.filterValue,
props.CalendarDates.fromDate,
props.CalendarDates.toDate
)
.then((response) => {
setdata(response.data.responses);
setloading(false); // HERE
})
.catch((error) => {
setdata([]);
setloading(false); // HERE
switch (error.response.status) {
case 403:
console.log("Error code --> " + 403);
props.history.push("/unAuthorizedPage");
break;
default:
console.log("Error String --->" + error);
}
});
};
https://www.npmjs.com/package/react-loading-overlay
Check this. I believe that this can solve your problem.
<LoadingOverlay
active={isLoading}
spinner={Your own spinner}
styles={{
wrapper: {
height: "100vh",
overflow: "scroll"
}
}}>
<div>Show your content here</div>
</LoadingOverlay>

Categories

Resources