Modify Axios request with inputs from React Modal - reactjs

I'm creating a React web app which would show a modal on every POST request asking users to add a approval token. This approval token would be added to the request header, before sending the request to the server.
I'm using axios to make a request as follows:
axios({
url: '/backend/call',
method: 'POST'
})
I'm intercepting the request through axios interceptors, to show the modal here. The modal has a text box and a submit button.
axios.interceptors.request.use(function (config) {
if (config.method === 'POST') {
ApprovalPopup.useApprovalPopup.showPopup(); // This displays the modal which has text field and Submit button
const token = --------; // How do I get the token from the Modal here, after submit is clicked.
config.headers.approvalToken = token;
}
return config;
});
Modal code for reference:
export type IApprovalPopup = {
showPopup: () => void;
};
const ApprovalPopupContext = createContext<IApprovalPopup>({
showPopup: () => undefined,
});
export default function ApprovalPopupProvider({children}: { children: ReactNode }) {
const dialogRef = useRef<HTMLDialogElement>(null);
const [ticketID, setTickedID] = useState('');
function handleSubmit() {
// update state and make backend call
}
function showPopup() {
dialogRef.current?.show()
}
return (
<ApprovalPopupContext.Provider
value={{
showPopup
}}
>
<span slot="body">
<div className="ticket-entry">
<Label> Ticket ID</Label>
<TextField onTextFieldChange={(event) => handleTicketIDEntry(event.detail.value)}
label="Ticket-ID"
value={ticketID}>
</TextField>
</div>
<div className="submit-section">
<div className="button">
<Button className="button" onClick={handleSubmit}>
Submit
</Button>
</div>
</div>
</span>
{children}
</ApprovalPopupContext.Provider>
);
}
ApprovalPopupProvider.useApprovalPopup = () => useContext(ApprovalPopupContext);
How do I get the token from the Modal and append it in the header before making the call to the server?
I could technically let the first call fail, and pass the config to the Modal as well. The Modal would then attach the header to the config and make the required call. But in that case how would I make sure the response goes back to the actual calling component. The calling component is updating local state objects / handling custom errors etc, so it needs the promise to be resolved

I think for you case it would be better to set axios header in handleSubmit function
simply add this :
function handleSubmit() {
axios.defaults.headers.common['approvalToken'] = token;
// update state and make backend call
}

Related

How can i use an AXIOS POST reponse from one component in another component?

Everything seems in order, the API is already functioning perfectly and returning a response with a JSON.
I need to get this JSON from the component that makes the post and gets the response to a component made to order this info and render it.
So far I have been able to access the response and get the arrays of information in the same component where the Axios post is, whenever I try to use the JSON from another component it won't recognize it
Should I combine these two components and make one? also, I have seen people using redux to store props globally and then using it in all of the ccomponents with no problem of recognition or prop-drilling.
I have read docs about redux. I understand how it works but as new as I am in it,I don't have a clue how to implement it
UPLOADER AND AXIOS POST COMPONENT
class SendFile extends Component {
state = {
// Initially, no file is selected
selectedFile: null
};
// On file select (from the pop up)
onFileChange = event => {
// Update the state
this.setState({ selectedFile: event.target.files[0] });
};
// On file upload (click the upload button)
onFileUpload = async () => {
// Create an object of formData
const formData = new FormData();
// Update the formData object
formData.append(
"file",
this.state.selectedFile,
);
// Details of the uploaded file
console.log(this.state.selectedFile);
// Request made to the backend api
// Send formData object
const headers = {
'access-token': "ACCES-TOKEN",
'filename': "file-name"
}
const diagnoseRequest = await axios.post("API",formData,{
headers:headers
});
console.log(diagnoseRequest.data.diagnose);
};
// File content to be displayed after
// file upload is complete
fileData = () => {
if (this.state.selectedFile) {
return (
<div>
<p>Nombre del archivo: {this.state.selectedFile.name}</p>
</div>
);
} else {
return (
null
);
}
};
render() {
return (
<Container>
<Uploader>
<input id="uploadBtn" accept="application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document" type="file" onChange={this.onFileChange} />
<button onClick={this.onFileUpload}>
Cargar Archivo
</button>
</Uploader>
{this.fileData()}
</Container>
);
}
}
export default SendFile;
RENDERING COMPONENT
function PdfSection({divider,img, title, data, text, subtext, text2, subtext2, text3, subtext3}) {
return (
<Container>
<Top>
<img className="divider" src={divider}/>
</Top>
<Bottom>
<LeftSide>
<img className='description' src={img}/>
</LeftSide>
<RightSide>
<Header>
<h1>{title}</h1>
</Header>
<Body>
<h2>{data}</h2>
<h3>{text}</h3>
<p>{subtext}</p>
<h3>{text2}</h3>
<p>{subtext2}</p>
<h3>{text3}</h3>
<p>{subtext3}</p>
</Body>
</RightSide>
</Bottom>
</Container>
)
}
export default PdfSection
THANKS IN ADVANCE!

Autocomplete through form

I was going to build autocomplete using remix.run, but then it occurred to me that would be relying too much on routing and forms to select/focus the input after each submit, this can not produce good UX. The user will input something into form, the form gets submitted, he awaits response, and then the input is focused again, and the ux here that this is instantsearch/autocomplete.
What you need is the useFetcher hook Remix exports. This hook let you fetch data from the loader of any route without causing a navigation, it was added for this kind of UIs.
import { Form, useFetcher } from "remix"
export default function Screen() {
let fetcher = useFetcher()
function handleChange(event) {
let value = event.currentTarget.value
// load data from a route with a loader
fetcher.load(`/api/autocomplete?query=${value}`)
}
return (
<Form>
<input type="text" onChange={handleChange} list="suggestions" />
<datalist id="suggestions">
{fetcher.data.map(item => {
return <option key={item.id} value={item.value} />
})}
</datalist>
</Form>
)
}
Something like that, and in the endpoint you load with the fetcher you export a loader
export async function loader({ request }) {
let url = new URL(request.url)
let query = url.searchParams.get("query") ?? "";
let data = await getData(query)
return json(data)
}

Why when using React useState is the payload data clearing on a onsubmit event? This is a POST request to the server side

Background: I am trying to get my frontend code to connect with my backend code.
Steps Taken:
--set a breakpoint for on click event for the submit button: this returns with the event being fired however no Promise function ran and no response in the console.
--tested sending data on Postman to backend and get status 200 and works (backend is set correctly and is hosted on heroku)
--cleaned up POST request and set it as a promise and stored in Const Variable
Steps the code is taking:
First taking in string in each input field (name, email, question).
Using react usestate hook to use the current state and set the event.target.value as those values.
Once that is stored, then it gets stored in a variable called custData
One submit button is clicked it calls on the Variable that stores the Promise function for POST request using Axios
(This is where I believe something else is occurring)
*the POST is set to the correct backend URL with the values of custData which should be a string. Then it should return with response.data in the console and does not.
Below is my frontend react code for this component:
import React from 'react';
import {useState} from 'react';
import Axios from 'axios'
//import { response } from 'express';
const QuoteForm = () => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [question, setQuestion] = useState("");
//This is the promise version
const custData =
{
"name" :name ,
"email" :email ,
"question":question
} ;
const submitPromise= () => {
console.log(custData);
Axios.post('https://hookahsite-backend.herokuapp.com || https://localhost:8000 ' , custData)
.then( (axiosResponse)=> {
// here you can use the data
console.log(custData);
const submitQuestions = axiosResponse.data;
console.log(submitQuestions);
})
.catch((e)=> {console.log(e)})
}
//this uses try catch however the backend is not getting hit with any data
//tested this same request in Postman and it works
/*
function submitQuestion() {
try {
Axios.post('https://hookahsite-backend.herokuapp.com ' ,
{
name:name ,
email:email ,
question:question
},
)
}
catch (err) {console.error(err);}
}
*/
return(
<React.Fragment>
<form id="quoteForm"
>
<h1 id="quoteTitle">Quote Help Form</h1>
<p id="quotePar">Please provide your Name, Contact Email, and what products you would like more information about in this form :</p>
<label id="formName" className="Form">
Name:
<input type="text" name="name"
onChange={(event) => { setName(event.target.value);}}
/>
</label>
<label id="formEmail" className="Form">
Email:
<input type="text" name="email"
onChange={(event) => { setEmail(event.target.value);
}}/>
</label>
<br/>
<label id="formQuestion" className="Form" >
What products would you like to know more about:
<input type="text" name="help"
onChange={(event) => { setQuestion(event.target.value);
}}/>
</label>
<br/>
<br/>
<button id="quoteSubmit" type="submit"
onClick =
{
submitPromise
}
/*
old way
{()=>
submitQuestion()
}
*/
>Submit </button>
</form>
›
</React.Fragment>
)
};
export default QuoteForm;
(When I set the breakpoints this is a screen shot showing the promise is there and there seems to be an issue with data being sent as possible text and not json format)
**Any Further Help on this topic would greatly be appreciated. **
The problem I believe lies in your Axios post call.
Axios is used like this:
Axios.post('ENDPOINT_URL', BODY)
Here ENDPOINT_URL is the URL of your api endpoint that you want to send a post request to, however you are saying to axios that the endpoint url is:
'https://hookahsite-backend.herokuapp.com || https://localhost:8000'
As far as I am concerned there is no logic in axios so it is trying to hit that string literally with a post request.
You should move your OR logic to other place of the application.
For example using env variables to know that you are running locally or on heroku you could do something like:
let url;
if (process.env.SERVER_LOCATION === "LOCAL") {
url = "https://localhost:8000";
} else{
url = "https://hookahsite-backend.herokuapp.com";
}
axios.post(url, custData).then( // etc

Stripe payment button for ReactJs with a simple Snippet Code

I have found a lot of repositories and examples about installing an Stripe complete checkout, but I cannot just use the simple Snippet code that Stripe offers for a product. How can I use that code on a page in my React Project? Here is the code. I just want to redirect the user to the Stripe checkout page for that product, I don't want to use my own formulary and I don't want either to collect data in my app. Thanks a lot.
<!-- Load Stripe.js on your website. -->
<script src="https://js.stripe.com/v3"></script>
<!-- Create a button that your customers click to complete their purchase. Customize the styling to suit your branding. -->
<button
style="background-color:#6772E5;color:#FFF;padding:8px 12px;border:0;border-radius:4px;font-size:1em"
id="checkout-button-price_1Heree568gerg54rtretrt"
role="link"
type="button"
>
Checkout
</button>
<div id="error-message"></div>
<script>
(function() {
var stripe = Stripe('pk_live_t5tyutrytutruytyutyufake....');
var checkoutButton = document.getElementById('checkout-button-price_1Heree568gerg54rtretrt');
checkoutButton.addEventListener('click', function () {
// When the customer clicks on the button, redirect
// them to Checkout.
stripe.redirectToCheckout({
lineItems: [{price: 'price_1Heree568gerg54rtretrt', quantity: 1}],
mode: 'subscription',
// Do not rely on the redirect to the successUrl for fulfilling
// purchases, customers may not always reach the success_url after
// a successful payment.
// Instead use one of the strategies described in
// https://stripe.com/docs/payments/checkout/fulfill-orders
successUrl: 'https://myweb.com/success',
cancelUrl: 'https://myweb.com/canceled',
})
.then(function (result) {
if (result.error) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer.
var displayError = document.getElementById('error-message');
displayError.textContent = result.error.message;
}
});
});
})();
</script>
You can create a dedicated component for that. As stated in the documentation, I am using StripeJS to import it as a module.
// npm install #stripe/stripe-js
import React from 'react';
import {loadStripe} from '#stripe/stripe-js';
const StripeButton = (props) => {
const [stripeError, setStripeError] = React.useState(null);
const [stripe, setStripe] = React.useState(null);
useEffect( async () => {
if (!stripe) {
// Here, you can use some `props` instead of hardcoding the API key
const stripeTmp = await loadStripe('pk_live_t5tyutrytutruytyutyufake....');
setStripe(stripeTmp);
}
});
const handleClick = () => {
// Reset error holder
setStripeError(null);
// When the customer clicks on the button, redirect
// them to Checkout.
stripe.redirectToCheckout({
// Here you can use another `prop` instead of hard coding it
lineItems: [{price: 'price_1Heree568gerg54rtretrt', quantity: 1}],
mode: 'subscription',
// Do not rely on the redirect to the successUrl for fulfilling
// purchases, customers may not always reach the success_url after
// a successful payment.
// Instead use one of the strategies described in
// https://stripe.com/docs/payments/checkout/fulfill-orders
successUrl: 'https://myweb.com/success',
cancelUrl: 'https://myweb.com/canceled',
})
.then(function (result) {
if (result.error) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer.
setStripeError(result.error.message);
}
});
}
return (
<>
{ stripe ? (
<button
style="background-color:#6772E5;color:#FFF;padding:8px 12px ;border:0;border-radius:4px;font-size:1em"
id="checkout-button-price_1Heree568gerg54rtretrt"
role="link"
type="button"
onClick={ handleClick }
>
Checkout
</button>
) : "Loading..."
}
{ stripeError ? <div id="error-message">{ stripeError }</div> : null }
</>
)
}
export default StripeButton;

React redirect after login not working

I am trying using react-router and redux to build my react app. However, I am unable to get the url to route back to the dashboard post login. Can somebody please point out my mistakes?
const form = reduxForm({
form: 'login'
});
class Login extends Component {
handleFormSubmit(formProps) {
this.props.loginUser(formProps);
var token = cookie.load('token');
if(token !== undefined){
const location = this.props.location;
if (location.state && location.state.nextPathname) {
browserHistory.push(location.state.nextPathname)
} else {
browserHistory.push('/')
}
}
}
renderAlert() {
if(this.props.errorMessage) {
return (
<div>
<span><strong>Error!</strong> Authentication error</span>
</div>
);
}
}
render() {
const { handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
{this.renderAlert()}
<div>
<label>Username</label>
<Field name="username" className="form-control" component="input" type="text" />
</div>
<div>
<label>Password</label>
<Field name="password" className="form-control" component="input" type="password" />
</div>
<button type="submit" className="btn btn-primary submitButton">Login</button>
</form>
</div>
);
}
}
function mapStateToProps(state) {
return {
errorMessage: state.auth.error,
message: state.auth.message
};
}
const mapDispatchToProps = dispatch => ({
loginUser: () =>
dispatch(loginUser);
});
export default connect(mapStateToProps, mapDispatchToProps)(form(Login));
My loginUser function is as below:
export function loginUser({ username, password }) {
return function(dispatch){
axios.post(`${AUTH_URL}/obtain-auth-token/`, { username, password}, {
headers: {
"X-CSRFToken": cookie.load('csrftoken')
}
})
.then(response => {
if('token' in response.data){
cookie.save('token', response.data.token, {path: '/'});
dispatch({type: AUTH_USER});
} else{
console.log("Error condiction: " + response);
errorHandler(dispatch, error.response, AUTH_ERROR);
}
})
.catch((error) => {
errorHandler(dispatch, error.response, AUTH_ERROR);
});
}
}
This is my first react-redux project so the mistake might be pretty elementary. Would really appreciate your help!
The root of your issue appears to be with handling your async call - in your handleFormSubmit(formProps) function you have the following two lines:
this.props.loginUser(formProps);
var token = cookie.load('token');
You are dispatching your action that will be running your async function (loginUser(formProps) does a post using axios), and then you immediately try to consume the results of this async function by loading the token that it should have stored in a cookie upon success. This does not work because immediately upon running an async function, JavaScript will not wait for the results but instead will return to your handleFormSubmit function and run the rest of it through completion. I am sure that if you console.log'd your token it will be undefined (assuming there were no cookies before running the app) - the function continued without waiting for your async function.
So I know two good options that you can use to fix this:
Simple, standard solution: You run dispatch({type: AUTH_USER}); upon the success of your async post - have this action creator result in a change in your state held by redux (ex: loginFlag). Include loginFlag as a prop in your Login component (include it in your mapStateToProps function). Finally, include a componentWillReceiveProps(nextProps) lifecycle function to your Login component and have it handle the route change. Include something like:
componetWillReceiveProps(nextProps) {
if (nextProps.loginFlag) {
var token = cookie.load('token');
if(token !== undefined){
const location = this.props.location;
if (location.state && location.state.nextPathname) {
browserHistory.push(location.state.nextPathname)
} else {
browserHistory.push('/')
}
}
}
}
Or:
Heavier solution using another package: You can use the npm package react-router-redux (https://github.com/reactjs/react-router-redux), to do a push to browserHistory within your redux action at the end of your async call - doing so will require you to setup its middleware and hook it up to your redux store. More info: https://github.com/reactjs/react-router-redux#api
I would definitely recommend looking into react-router-redux. If you do not want the browserHistory functions available in your redux actions, then it is very simple to setup in your project (don't need to make changes to the store or add its middleware) and it allows you to track your url path within your redux state which can be very helpful.

Resources