How to add validation for status code 400 in react - reactjs

I am trying to edit a software program that requires a validation for an error on "Uncaught (in promise) Error: Request failed with status code 400"
Reason for this is that the user is trying to create a same unique data on the table. that is why it's getting a status code 400.
So now, I want to have a error handler for the status code 400 and will return a String in a text field stating "Duplicate Entry"
Here are my current validations.
import * as Yup from "yup";
const InvoiceValidationSchema = Yup.object().shape({
customer: Yup.object()
.required("Customer is required."),
invoiceDate: Yup.string()
.required("Invoice Date is required"),
dueDate: Yup.string()
.required("Due Date is required."),
invoiceNumber: Yup.string()
.required("Invoice Number is required"),
invoiceLines: Yup.array()
.of(
Yup.object().shape({
product: Yup.object().required("Required"),
quantity: Yup.number().required("Required.").min(1, "Quantity cannot be zero."),
// price: Yup.number().required("Required.").min(1, "Quantity cannot be zero.").typeError("")
})
).required("Required")
});
export default InvoiceValidationSchema;
I hope anyone can help me. Thanks
Here is the formik
<Formik
validationSchema={InvoiceValidationSchema}
initialValues={invoiceCrud}
onSubmit={(values, action) => {
const invoiceLines = values.invoiceLines.map(invoiceLine => {
return {
...invoiceLine,
product: invoiceLine.product,
price: invoiceLine.price,
quantity: invoiceLine.quantity,
totalAmount: invoiceLine.quantity * invoiceLine.price,
averagePurchasePrice: invoiceLine.product.averagePurchasePrice
}
})
const subTotal = invoiceLines.reduce((sum, line) => {
return sum + line.totalAmount;
},0)
const totalExpense = invoiceLines.reduce((sum, line) => {
return sum + (line.product.averagePurchasePrice * line.quantity);
},0)
const totalAdjustment = 0;
const netTotal = subTotal - totalAdjustment;
const totalDueAmount = subTotal - values.subTotal + values.totalDueAmount;
const invoice ={
...values,
invoiceLines,
subTotal,
totalAdjustment,
netTotal,
totalExpense,
totalDueAmount
}
if(values.invoiceLines[0].price > 0 && values.invoiceLines[0].quantity > 0){
submit(invoice);
setTimeout(() => {
action.setSubmitting(false);
}, 1000);
}else{
setIsZero(true)
action.setSubmitting(false)
}
}}
render={
({ values, errors, touched, setFieldValue, isSubmitting }) => (
<Form>
<div className="box-body" id="invoice-crud">
<div className="row">
<div className="col-lg-3 col-md-6 col-sm-12">
{errors.invoiceLines ? setIsProductSelected(false) : null}
{errors.customer ? setCustomerClass("btn btn-default dropdown-toggle transaction-form__account has-error")
: setCustomerClass("btn btn-default dropdown-toggle transaction-form__account")}
<DropdownWithSearch
property={
{
title: "Customer",
buttonClass: CustomerClass,
buttonLabel: values.customer ? values.customer.partnerName : "Select Customer",
newButtonLabel: "Customer",
showRemove: values.customer,
modalSelector: "#partner-form"
}
}
option={{
list: partnerDetails.list,
total: partnerDetails.total,
currentPage: partnerDetails.currentPage,
pageSize: partnerDetails.pageSize,
load: changePartnerPage,
display: (element) => element.partnerName,
onClick: (element) => {
setFieldValue("customer", element)
},
removeSelected: () => {
setFieldValue("customer", null)
},
search: (search) => {
dropdownSearch(search, "partner")
}
}}
/>{errors.customer ? <span className="errorMessage">Please select a customer</span> : null}
</div>
<FormRow validation={{errors: errors, touched: touched}} field={invoiceNumber}/>
</Form>
)}
</Formik>

Question seems to be related to Formik and handling backend errors in it, rather than anything else.
You can check how to set manually backend errors in formik in this github issue.
And in particular this sandbox, posted by user #t-nunes in github.
If your function which makes the request is written in the new async/await style, you'll probably handle your errors in a try / catch block, and for the old .then, .catch style, you'll be using the .catch callback as long as server returns error code.

Related

Formik form won't submit multiple times consecutively (CRUD app built with React and Firebase)

I've built a recipe app with React and Firebase, and I'm encountering a bug where I'm unable to edit a selected recipe more than once in a row. I can edit it once, but if I open the edit form again and try to submit it, it doesn't go through.
If I select a different recipe (even without reopening the form) and then return to the original one, I'm able to submit an edit again without issue.
When the submission fails, I see the following warning in the console:
Warning: An unhandled error was caught from submitForm() TypeError: Cannot read properties of undefined (reading 'indexOf')
at Function.value (index.esm2017.js:1032:19)
at Qm (index.esm2017.js:16035:32)
at firestore.js:80:17
at c (regeneratorRuntime.js:72:17)
at Generator._invoke (regeneratorRuntime.js:55:24)
at Generator.next (regeneratorRuntime.js:97:21)
at Ve (asyncToGenerator.js:3:20)
at o (asyncToGenerator.js:22:9)
at asyncToGenerator.js:27:7
at new Promise (<anonymous>)
Here's the GitHub repo, and the live site. Use the "demo" button to log in anonymously.
I apologize if this is too broad. I toyed with trying to recreate the issue on a smaller scale in CodeSandbox, but to do so, I think I'd have to rebuild a large portion of the app.
Because the issue resolves upon selecting a different recipe, I suspect it has to do with the selectedRecipe value in the app's state. However, I'm not sure exactly where the "indexOf" in the console warning is coming from.
The recipe form component:
import { useContext, useState, useEffect } from "react";
import { recipeContext } from "../../context/recipeContext";
import { Formik, Field, Form } from "formik";
import { modes } from "../modals/modalModes";
import { addRecipeToDb, updateRecipeInDb } from "../../firebase/firestore";
import * as Yup from "yup";
function RecipeForm({ modalMode, toggleModal, user }) {
const { selectedRecipe, setSelectedRecipe } = useContext(recipeContext);
const [formValues, setFormValues] = useState(null);
// Array => string separated by newlines
const convertFromArray = (array) => {
let string = "";
array.forEach((str, index) => {
if (index < 1) {
string = string + str;
} else string = string + "\n" + str;
});
return string;
};
// String separated by newlines => array
const convertToArray = (string) => {
return string.split("\n");
};
const handleSubmit = async (values) => {
const recipe = {
title: values.title,
description: values.description,
ingredients: convertToArray(values.ingredients),
directions: convertToArray(values.directions),
uid: user.uid,
};
if (modalMode === modes.create) {
recipe.labels = [];
recipe.createdAt = new Date();
await addRecipeToDb(recipe);
setSelectedRecipe(recipe);
console.log(recipe);
} else if (modalMode === modes.edit) {
recipe.labels = selectedRecipe.labels;
recipe.createdAt = selectedRecipe.createdAt;
await updateRecipeInDb(recipe, selectedRecipe.id);
setSelectedRecipe(recipe);
}
toggleModal();
};
const initialValues = {
title: "",
description: "",
ingredients: "",
directions: "",
};
useEffect(() => {
if (selectedRecipe && modalMode === modes.edit) {
setFormValues({
title: selectedRecipe.title,
description: selectedRecipe.description,
ingredients: convertFromArray(selectedRecipe.ingredients),
directions: convertFromArray(selectedRecipe.directions),
});
}
}, [modalMode, selectedRecipe]);
const validationSchema = Yup.object({
title: Yup.string()
.max(80, "Title must be less than 80 characters")
.required("Required"),
description: Yup.string().max(
400,
"Description must be less than 400 characters"
),
ingredients: Yup.string()
.max(
10000,
"That's a lot of ingredients! The limit is 10,000 characters."
)
.required("Required"),
directions: Yup.string()
.max(
10000,
"This recipe is too complicated! The limit is 10,000 characters."
)
.required("Required"),
});
return (
<Formik
initialValues={formValues || initialValues}
onSubmit={handleSubmit}
validationSchema={validationSchema}
enableReinitialize>
{({ errors, touched }) => (
<Form id="recipe-form" className="form recipe-form">
<div className="field-wrapper">
<label htmlFor="title">Title</label>
<div className="error">{touched.title ? errors.title : null}</div>
<Field id="title" name="title" placeholder="Cake" as="input" />
</div>
<div className="field-wrapper">
<label htmlFor="description">Description</label>
<div className="error">
{touched.description ? errors.description : null}
</div>
<Field
id="description"
name="description"
placeholder="A real cake recipe"
as="textarea"
/>
</div>
<div className="field-wrapper">
<label htmlFor="ingredients">Ingredients</label>
<p className="error">
{touched.ingredients ? errors.ingredients : null}
</p>
<Field
id="ingredients"
name="ingredients"
placeholder="milk
eggs
flour
sugar"
as="textarea"
/>
<p className="message">Type each ingredient on a new line</p>
</div>
<div className="field-wrapper">
<label htmlFor="directions">Directions</label>
<p className="error">
{touched.directions ? errors.directions : null}
</p>
<Field
id="directions"
name="directions"
placeholder="Mix everything together
Bake at 350 degrees for 30 minutes
Let cool
Serve"
as="textarea"
/>
<p className="message">Type each step on a new line</p>
</div>
</Form>
)}
</Formik>
);
}
The function that updates the recipe in Firestore:
const updateRecipeInDb = async (recipeObj, id) => {
const docRef = doc(db, "recipes", id);
try {
updateDoc(docRef, recipeObj);
} catch (e) {
console.error("Error updating document: ", e.message);
}
};
I've messed around with several things over the past few days. Adding selectedRecipe to the form component's useEffect dependency list didn't make a difference, nor did removing the validation schema. I just can't figure out exactly what's going wrong, or where the problem is occurring. Has anyone encountered something similar, or is there something obvious I'm missing?

React .NET - Display data on page from a POST

I am working on a calculating application using ASP.NET Core 6 and React 18
in the .NET application, I am using some logic to return some data, the data is not stored in the database, only temporary/mock data and I am only using POST.
the code in the backend works, it returns what I want.
However, in my frontend application with React. I am using Formik for formhandeling, and it works when entering the data my backend wants in the post endpoint.
When adding a breakpoint to the endpoint it works just as I want.
But I also want the returned data to be visible in my React application.
Let me show you some code.
Backend
[HttpPost("Calculate")]
public ActionResult<TaxModel> PostCalculation(TaxModel model)
{
DateTime date;
if (model == null || !DateTime.TryParseExact(model.Date + " " + model.Time + ":00", "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
{
return BadRequest(BadRequestMsg);
}
try
{
var vehicle = model.Vehicle;
var calculatePrice = Calculator.GetTollFee(date, vehicle);
return Ok(new TaxModel
{
Date = model.Date,
Time = model.Time,
Vehicle = model.Vehicle,
Price = calculatePrice
});
}
catch
{
return BadRequest(BadRequestMsg);
}
}
Frontend
export const TaxForm = (props: taxFormProps) => {
return(
<Formik initialValues={props.model}
onSubmit={props.onSubmit}
validationSchema={yup.object({
date: yup.string().required("Date is required"),
time: yup.string().required("Time is required"),
vehicle: yup.string().required("Vehicle is required"),
})}>
{(FormikProps) => (
<Form>
<InputField field="date" displayName="Date" />
<InputField field="time" displayName="Time" />
<InputField field="vehicle" displayName="Vehicle" />
<Button className="" type="submit"
text="Calculate" disabled={FormikProps.isSubmitting} />
</Form>
)}
</Formik>
)
}
interface taxFormProps {
model: taxModel;
onSubmit: (values: taxModel, actions: FormikHelpers<taxModel>) => void;
}
export const PostTax = () => {
const [errors, setErrors] = useState<string[]>([]);
const [tax, setTax] = useState<taxModel>();
const create = async (tax: taxModel) => {
try{
await axios.post(`${UrlTax}/Calculate`, tax)
.then(response => {
if(response.status === 200){
setTax(response.data);
}
});
}
catch(error) {
if(error && error.response){
setErrors(error.response.data);
}
}
}
return(
<>
<h3>Calculate your toll Price</h3>
<DisplayErrors errors={errors} />
<TaxForm model={
{
date: "",
time: "",
vehicle: "",
price: 0
}}
onSubmit={async value => {
await create(value);
}} />
{/* {tax?.map(t =>
<p>{t.date} {t.time} {t.vehicle} {t.price}</p>
)} */}
<p>{tax}</p> // does not work
</>
)
}
in the React code, I am trying to add the posted data into the state, but it is not really working.
Any idea on how to fix this error?
Thanks!

Pass Selected Values to a Get request

My goal is to pass values selected in a form, to a get request.
The request should look as follows, following submit of the values.
get(api/csv/pets/?columns=DOG&columns=CAT&columns=FISH)
onSubmit={async (values, { resetForm }) => {
alert(JSON.stringify(values, null, 2));
setCsvData(values);
console.log(csvData);
const getCsvFile = async (values) => {
try {
const { data } = await fetchContext.authAxios.get(
`/api/csv/pets/${id}/?columns=${csvData ? csvData + '&' : null}`, values);
toCsv(data);
} catch (err) {
console.log(err);
}
};
getCsvFile();
However, even though formik takes a payload of values, I still get undefined when placing it in csvData with setCsvData(values).
What can I do to get the values selected in the query, and in the format needed?
My data:
export const CheckList = [
{
id: 'column-dog',
label: 'Dog',
value: 'DOG',
name: 'column',
},
{
id: 'column-cat',
label: 'Cat',
value: 'CAT',
name: 'column',
},
{
id: 'column-turtle',
label: 'Turtle',
value: 'TURTLE',
name: 'column',
},
{
id: 'column-fish',
label: 'Fish',
value: 'FISH',
name: 'column',
},
]
My Form:
const [csvData, setCsvData] = useState([]);
const selectAllData = CheckList.map((checkbox) => checkbox.value);
return (
<Formik
initialValues={{
columns: [],
selectAll: false,
}}
onSubmit={async (values, { resetForm }) => {
alert(JSON.stringify(values, null, 2));
setCsvData(values);
console.log(csvData);
resetForm();
}}
>
{({ values, setFieldValue }) => (
<Form>
<div>
<Field
onChange={() => {
if (!values.selectAll) {
setFieldValue('columns', selectAllData);
} else {
setFieldValue('columns', []);
}
setFieldValue('selectAll', !values.selectAll);
}}
checked={values.selectAll}
type="checkbox"
name="selectAll"
/>{' '}
Select All
</div>
{CheckList.map((checkbox) => (
<div key={checkbox.value}>
<label>
<Field
type="checkbox"
name="columns"
value={checkbox.value}
/>{' '}
{checkbox.label}
</label>
</div>
))}
<button
className="btn"
type="submit"
download
>
DOWNLOAD CSV
</button>
</Form>
)}
</Formik>
)
I think you've over-complicated your code a little bit. Remove csvData from state, and instead pass values into request.
onSubmit={async (values, { resetForm }) => {
const getCsvFile = async () => {
try {
const { data } = await fetchContext.authAxios.get(
`/api/csv/pets/${id}/?columns=${values ? values + '&' : null}`, values);
toCsv(data);
} catch (err) {
console.log(err);
}
};
getCsvFile();
}
You were getting an error is because the request was firing before csvData was set. setState is asynchronous :)
You cannot use setState inside an async function, becuase setState is an async function itself and it doesn't return a promise. As a result, we can't use async/await for setState method.(If it's not possible to make it await, then state change will trigger right away with undefined values instead of setting the given values)
Please refer this for more info: https://iamsongcho.medium.com/is-setstate-async-b1947fbb25e5
Therefore, the best use case is to remove the async from onSubmit() function to make it synchronous. As a result, setState will trigger the state change in component asynchronously.
Then keep getCsvFile(values), after form reset as you've already done that. And keep getCsvFile() function outside the component, since it doesn't do any state relevant changes.
onSubmit={(values, { resetForm }) => {
alert(JSON.stringify(values, null, 2));
setCsvData(values);
console.log(csvData);
resetForm();
getCsvFile(); // this function will trigger asynchronously
}}

React-stripe-checkout Getting the shipping address

I want to put a property in the schema to receive the delivery address they could help me with what I need to be editing I didn't find it in any documentation, but something like metadata: args but I didn't find a way to put it in the code please help me thanks,
in models / Order.js I would like to register the shipping address in the Order, in the pages / api / checkout.js file when registering the new order:
await new Order ({
user: userId,
email: paymentData.email,
total: cartTotal,
products: cart.products
}). save ();
I would like to have an address field that will receive the delivery address of the checkout stripe form like this:
await new Order ({
user: userId,
email: paymentData.email,
address: "shipping address"
total: cartTotal,
products: cart.products
}). save ();
in the Order model:
import mongoose from 'mongoose';
import Product from './Product';
const {ObjectId, String, Number} = mongoose.Schema.Types;
const OrderSchema = new mongoose.Schema({
user: {
type: ObjectId,
ref: "User"
},
products: [
{
quantity: {
type: Number,
default: 1
},
product: {
type: ObjectId,
ref: Product
}
}
],
email: {
type: String,
required: true
},
total: {
type: Number,
required: true
},
status: {
type: String,
required: true,
default: 'pending',
enum: ["pending", "delivered"]
}
}, {
timestamps: true
});
export default mongoose.models.Order || mongoose.model("Order", OrderSchema);
I want to put a property in the schema to receive the delivery address they could help me with what I need to be editing I didn't find it in any documentation, but something like metadata: args but I didn't find a way to put it in the code please help me thanks
import React from 'react';
import StripeCheckout from 'react-stripe-checkout';
import { Button, Segment, Divider } from 'semantic-ui-react';
import calculateCartTotal from '../../utils/calculateCartTotal';
const CartSummary = ({ products, handleCheckout, success }) => {
const [cartAmout, setCartAmaount] = React.useState(0);
const [stripeAmount, setStripeAmount] = React.useState(0);
const [isCartEmpty, setCartEmpty] = React.useState(false);
React.useEffect(() => {
const {cartTotal, stripeTotal} = calculateCartTotal(products);
setCartAmaount(cartTotal);
setStripeAmount(stripeTotal);
setCartEmpty(products.length === 0)
}, [products]);
return (
<React.Fragment>
<Divider />
<Segment clearing size="large">
<strong>Sub total:</strong>R$ {cartAmout}
<StripeCheckout
name="Conv. Calegari"
amount={stripeAmount}
image={products.length > 0 ? products[0].product.mediaUrl : ""}
currency="BRL"
shippingAddress={true}
billingAddress={true}
locale={'pt'}
zipCode={true}
stripeKey="pk_test_51H9DjUDvVpK1u7JvauE1t2VkVYyda6CGrlVy8az40uO9ELwXmQ3NFghlLIcTJDInZIazUlxLGMuUaMHhRjArCPB9008RvaFo7N"
token={handleCheckout}
triggerEvent="onClick"
>
<Button
icon="cart"
color="green"
floated="right"
content="Checkout"
disabled={isCartEmpty || success}
/>
</StripeCheckout>
</Segment>
</React.Fragment>
);
}
export default CartSummary;
Here I am using the stripe checkout and I need to somehow get this token with the address and save it in order

React-Bootstrap form validation - Need one function per field?

I am using the React-Bootstrap forms. I have around 15 fields that need to be filled out in the form. Does this mean I need to have 15 validation functions (e.g validateName, validateDate etc.)?
How is this generally approached?
My data looks something like this:
state = {
person : {
name: '',
startDate: null,
...
...
active: null
}
}
Say for eg you have 2 input fields
state = {
person : {
name: '',
age: 0
},
nameError: null,
ageError: null
}
handleInput = e => {
const { person } = this.state;
person[e.target.name] = e.target.value;
this.setState({
person
});
}
handleSubmit = () => {
const { person } = this.state;
if(person.name === null){
this.setState({
nameError: 'Name is required',
ageError: null
});
}else if(person.age === 0){
this.setState({
ageError: 'Age is required',
nameError: null
});
}else{
//send the values to the backend
//also reset both nameError and ageError here
}
}
render(){
const { person, nameError, ageError } = this.state;
return(
<div>
<input type='text' name='name' value={person.name} onChange={e => this.handleInput(e)} />
{nameError}
<input type='number' name='age' value={person.age} onChange={e => this.handleInput(e)} />
{ageError}
<button value='Submit' onClick={this.handleSubmit} />
</div>
);
}
Please Let me know if you have further queries. Sorry if there are any typos I answered on my mobile

Resources