MUI TextField breaks Validation Schema when addind Onchange - reactjs

this is my first thread I hope i do this right,
I am trying to send data for mysql server and it goes well, however when I use TextField from MUI the validation schema starts looking funny and gives error ("require field") even with the text there, However if i submit the data the mysql receives the data.
and here goes the code...
//uses Express send data to mysql
import { useState, useEffect } from 'react'
import axios from "axios"
import { Container, Grid, Typography} from '#material-ui/core';
import { makeStyles } from '#material-ui/core/styles';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import Textfield from './Forms/IndividualComponents/TextField';
import Button from './Forms/IndividualComponents/Button'
const useStyles = makeStyles((theme) => ({
formWrapper: {
marginTop: theme.spacing(10),
marginBottom: theme.spacing(8),
},
}));
const INITIAL_FORM_STATE = {
firstName: ''
};
const FORM_VALIDATION = Yup.object().shape({
firstName: Yup.string("enter your first name")
.required('Required')
});
export default function ImagesUpload() {
const [firstName, setFirstName] = useState()
const [submitform, setSubmitForm] = useState([])
useEffect(() => {
(async() => {
const result = await axios.get('/submitform')
setSubmitForm(result.data.submitform)
})()
}, [])
const submit = async event => {
event.preventDefault()
const data = new FormData()
data.append('firstName', firstName)
const result = await axios.post('/submitForm', data)
setSubmitForm([result.data, ...submitform])
console.log(firstName)
}
const classes = useStyles();
return (
<Grid item xs={12}>
<Container maxWidth="md">
<div className={classes.formWrapper}>
<Formik
initialValues={{
...INITIAL_FORM_STATE
}}
validationSchema={FORM_VALIDATION}
>
<Form onSubmit={submit}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography>
Personal details
</Typography>
</Grid>
<Grid item xs={6}>
<Textfield
name="firstName"
label="First Name"
value={firstName}
onChange={(event => setFirstName(event.target.value) )}
type="text"
/>
</Grid>
<button type="submit">Submit</button>
</Grid>
</Form>
</Formik>
<main>
{submitform.map(post => (
<figure key={post.id}>
<figcaption>{post.firstName}</figcaption>
</figure>
))}
</main>
</div>
</Container>
</Grid>
);
}
the Textfield im using is a costum one with the following code:
import React from 'react';
import { TextField } from '#material-ui/core';
import { useField } from 'formik';
const TextfieldWrapper = ({
name,
...otherProps
}) => {
const [field, mata] = useField(name);
const configTextfield = {
...field,
...otherProps,
fullWidth: true,
};
if (mata && mata.touched && mata.error) {
configTextfield.error = true;
configTextfield.helperText = mata.error;
}
return (
<TextField {...configTextfield} />
);
};
export default TextfieldWrapper;
the problem here is the onChange: If i don't use it I cant setFirstName to send the data, but when I use it it breaks the validation... I tried everything i can find online but this is taking away my sleep for real ! :)

Related

react-number-format doesn't work with Formik and Mui

I tried to use react-number-format with mui TextField and Formik, when I submit the form I get a required error. Its seems that nested TextFiled with react-number-format can't get the values.
I tried to console log the input but whit react-number-format I get empty string.
//App.jsx
import * as React from "react";
import { Typography, InputAdornment, Grid } from "#mui/material";
import { Formik, Form } from "formik";
import * as Yup from "yup";
import { NumericFormat } from "react-number-format";
import axios from "axios";
import { FieldWrapper as TextField } from "./InputTem";
import { BtnTemp as Button } from "./Btn";
import "./styles.css";
const FORM_STATE = {
price: ""
};
const priceProps = {
name: "price",
label: "Price",
InputProps: {
startAdornment: <InputAdornment position="start">€</InputAdornment>
},
required: true
};
const FORM_VALIDATION = Yup.object().shape({
price: Yup.number().required("require")
});
const App = () => {
return (
<Grid container flex justifyContent={"center"}>
<Formik
initialValues={{ ...FORM_STATE }}
validationSchema={FORM_VALIDATION}
onSubmit={(values) => {
console.log(values);
}}
>
<Form>
<Grid
container
spacing={2}
rowGap={1}
padding={3}
maxWidth={1000}
justifyContent={"center"}
>
<Grid item xs={12}>
<Typography
variant="h3"
component="h1"
align="center"
color="#280982"
>
Price Validation Error
</Typography>
</Grid>
<Grid item xs={12}>
<Grid item xs={12}>
<NumericFormat
name="price"
id="price"
thousandSeparator=","
allowNegative={false}
decimalScale={2}
decimalSeparator="."
fixedDecimalScale={true}
customInput={TextField}
{...priceProps}
/>
</Grid>
<Grid container marginTop="1rem">
<Button>submit</Button>
</Grid>
</Grid>
</Grid>
</Form>
</Formik>
</Grid>
);
};
export default App;
// Input.jsx
import * as React from "react";
import TextField from "#mui/material/TextField";
import { useField } from "formik";
export const FieldWrapper = ({ name, ...otherProps }) => {
const [field, meta] = useField(name);
if (meta && meta.touched && meta.error) {
otherProps.error = true;
otherProps.helperText = meta.error;
}
const configText = {
...field,
...otherProps,
fullWidth: true,
variant: "outlined"
};
return <TextField {...configText} />;
};
export default FieldWrapper;
//BtnTemplate.jsx
import { Button } from "#mui/material";
import { useFormikContext } from "formik";
export const BtnTemp = ({ children, ...otherProps }) => {
const { submitForm } = useFormikContext();
const formHandler = () => {
submitForm();
};
const btnConfig = {
...otherProps,
fullWidth: true,
variant: "outlined",
onClick: formHandler
};
return <Button {...btnConfig}>{children}</Button>;
};
andhere is a link for CodeSandbox
Thanks in advancce !
Now its works ! just passed the usedField props.
here is the solution in sandbox
import * as React from "react";
import { NumericFormat } from "react-number-format";
import { useField } from "formik";
const PriceTemp = ({ name, ...otherProps }) => {
const [field, meta] = useField(name);
const passwordConfig = {
...field,
...otherProps,
name: "price",
label: "Price",
thousandSeparator: ",",
allowNegative: false,
decimalScale: 2,
decimalSeparator: ".",
fixedDecimalScale: true
};
return <NumericFormat {...passwordConfig} />;
};
export default PriceTemp
;

React Hook Form w/ FormProvider and MUI - Not working

I have been trying to abstract my form components for some time now. I took the example of RHF JS and rewrote it in Typescript.
Unfortunately, I do not get any output of the validation results. When I submit the form nothing happens either. My understanding is that I need to pass the FormContext through to the last component.
Can anyone with experience help me?
App.tsx
import { yupResolver } from "#hookform/resolvers/yup";
import { Button, Grid, TextField } from "#mui/material";
import { memo } from "react";
import {
FormProvider,
useForm,
useFormContext,
UseFormReturn,
} from "react-hook-form";
import { object, SchemaOf, string } from "yup";
type FormData = {
name: string;
};
const schema: SchemaOf<FormData> = object().shape({
name: string()
.required("Input is Required")
.matches(/^[a-zA-Z0-9]+$/, "Only alphanummeric"),
});
type TextInputProps = {
methods: UseFormReturn;
name: string;
label: string;
};
// we can use React.memo to prevent re-render except isDirty state changed
const TextFieldMemo = memo(
({ methods, name, label }: TextInputProps) => (
<TextField
label={label}
variant="outlined"
error={!!methods.formState.errors[name]}
helperText={
(methods.formState.errors[name]?.message as unknown as string) ?? ""
}
fullWidth
margin="normal"
{...methods.register(name)}
InputLabelProps={{
shrink: true,
}}
FormHelperTextProps={{
sx: {
position: "absolute",
bottom: "-1.5rem",
},
}}
/>
),
(prevProps, nextProps) =>
prevProps.methods.formState.isDirty ===
nextProps.methods.formState.isDirty &&
prevProps.methods.formState.errors !== nextProps.methods.formState.errors
);
export const TextFieldMemoContainer = () => {
const methods = useFormContext();
return <TextFieldMemo methods={methods} name="name" label="Name" />;
};
export default function App() {
const methods = useForm<FormData>({
resolver: yupResolver(schema),
mode: "onChange",
});
const { isDirty } = methods.formState;
const onSubmit = (form: FormData) => {
console.log(isDirty);
console.log(form);
};
return (
<Grid container>
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<Grid item>
<TextFieldMemoContainer />
</Grid>
<Grid item>
<Button
type="submit"
disabled={
!methods.formState.isDirty || !methods.formState.isValid
}
>
Submit
</Button>
</Grid>
</form>
</FormProvider>
</Grid>
);
}
Thank you all in advance.
StackBlitz MRE

Formik: POST url/salary-scales 400 (Bad Request)

I have this project and it is a project to run a contracting company, and I am trying to create an interface in order to allow the admin to create a salary scale, and this is what the requestlooks like on Swagger:
And as it turns out, I have array of Objects.
And for that, I used a formik and sent the data to the backend
But I got this error:
POST https://ite-ria.herokuapp.com/api/v1/salary-scales 400 (Bad Request)
In addition, the data is not filled with data
This is what the network looks like in the browser:
How can i solve the problem?
file1.js:
import React, { useState } from "react";
import TextField from "#material-ui/core/TextField";
import Grid from "#material-ui/core/Grid";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
// import { addInvoice } from "../../../store/invoiceSlice";
import { motion } from "framer-motion";
import { useDispatch } from "react-redux";
import "react-datepicker/dist/react-datepicker.css";
import Slide from "#material-ui/core/Slide";
import { useSnackbar } from "notistack";
import { getJobs, addSalaryScale } from "../../store/salaryScaleSlice";
import { useEffect } from "react";
import InputAdornment from "#material-ui/core/InputAdornment";
import WorkOutlineOutlinedIcon from "#material-ui/icons/WorkOutlineOutlined";
import Input from "#material-ui/core/Input";
import AccountTreeOutlinedIcon from "#material-ui/icons/AccountTreeOutlined";
import { FieldArray, Field, Form, Formik } from "formik";
import { Card, CardContent } from "#material-ui/core";
import Autocomplete from "#material-ui/lab/Autocomplete";
import PostAddIcon from "#material-ui/icons/PostAdd";
const useStyles = makeStyles((theme) => ({
root: {
"& > *": {
margin: theme.spacing(1),
},
},
input: {
display: "none",
},
button: {
margin: theme.spacing(1),
// padding: theme.spacing(4),
},
}));
function ShippingTab(props) {
const dispatch = useDispatch();
const classes = useStyles();
// const [amount, setAmount] = useState("");
const [jobs, setJobs] = useState([]);
// const [jobId, setJobId] = useState(0);
// const [employeeLevel, setEmployeeLevel] = useState("");
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const handleCreateInvoiceMessageClick = () => {
enqueueSnackbar(
"Invoice created successfully",
{ variant: "success" },
{
anchorOrigin: {
vertical: "top",
horizontal: "right",
},
},
{ TransitionComponent: Slide }
);
};
const handleAmountChange = (event) => {
setAmount(event.target.value);
};
useEffect(() => {
getJobs().then((response) => {
console.log("jobs response in approve: ", response);
// setJobs(response);
});
}, []);
return (
<Card>
<CardContent>
<Formik
initialValues={{
entities: [{ jobId: 0, amount: 0, employeeLevel: " " }],
}}
onSubmit={async (values) => {
console.log("values: ", values);
return dispatch(addSalaryScale(values));
}}
>
{({ values, isSubmitting }) => (
<Form autoComplete="off">
<Grid container>
<Grid item>
<FieldArray name="entities">
{({ push, remove, Formik }) => (
<React.Fragment>
{values?.entities.map((_, index) => (
<Grid container item key={index}>
<Grid item>
<Field
name={`entities[${index}].jobId`}
id="entities.jobId"
component={TextField}
label="JobId"
/>
</Grid>
<Grid item>
<Field
name={`entities[${index}].amount`}
id="entities.amount"
component={TextField}
type="number"
label="amount"
/>
</Grid>
<Grid item>
<Field
name={`entities[${index}].employeeLevel`}
id="entities.employeeLevel"
component={TextField}
label="employeeLevel"
/>
</Grid>
<Grid item>
<Button onClick={() => remove(index)}>
Delete
</Button>
</Grid>
</Grid>
))}
<Grid item>
<Button
onClick={() =>
push({ jobId: 0, amount: 0, employeeLevel: "" })
}
>
Add
</Button>
</Grid>
</React.Fragment>
)}
</FieldArray>
</Grid>
</Grid>
<pre>{JSON.stringify({ values }, null, 4)}</pre>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</CardContent>
</Card>
);
}
export default ShippingTab;
salaryScaleSlice.js:
export const addSalaryScale = createAsyncThunk(
"salaryScalesApp/salaryScale/addSalaryScale",
async (salaryScale, { dispatch, getState }) => {
console.log('salary Scale: ', salaryScale)
const response = await axios.post("/salary-scales", salaryScale);
const data = await response.data.data;
console.log("Hi I am Here in add new Job: ", data);
dispatch(getSalaryScales());
return data;
}
);
400 means you have an error in your Request (i.e. wrong url-param, missing body, etc.). -> https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400
Since the Response shows you that 'entities' is an invalid field my guess is that some of the fields (like 'jobId') are required for the API-server to work probably. But without further knowledge or a link to the swagger documentation this is just a guess.
Best approach would be to contact the admin of the contracting company and ask him to send you a correctly formatted and working request so that you have some test-data.
...hope this was any helpful

API data not printing but successes with console.log

I'm trying to learn about APIs and trying to code a REACT app to go along with it. I am sure the issue is a minor one, but I can't seem to crack it.
The relevant code is pasted below, the API is fetched in index.js.
The contents of the API is printed to the console without issue but I can not seem to get it right when going through my list and event details.
I am new to coding so I would appreciate any feedback given.
App.js
import React, { useState, useEffect } from "react";
import { CssBaseline, Grid } from "#material-ui/core";
import { getEventsData } from "./api";
import Header from "./components/Header/Header";
import List from "./components/List/List";
import EventDetails from "./components/EventDetails/EventDetails";
const App = () => {
const [events, setEvents] = useState([]);
useEffect(() => {
getEventsData()
.then((data) => {
console.log(data);
console.log(Array.isArray(data))
setEvents(data);
})
}, []);
return (
<>
<CssBaseline />
<Header />
<List EventDetails={EventDetails} />
</>
)
}
export default App;
index.js
import axios from "axios";
const URL = 'https://api-football-v1.p.rapidapi.com/v3/fixtures'
const options = {
params: {date: '2022-02-12', league: '39', season: '2021'},
headers: {
'x-rapidapi-host': 'api-football-v1.p.rapidapi.com',
'x-rapidapi-key': xxxXXXxxxXXXxxx'
}
};
export const getEventsData = async () => {
try {
const { data } = await axios.get(URL, options);
// Kan det ha något med options att göra? https://stackoverflow.com/questions/68367352/multiple-url-variable-async-await-axios
return data;
} catch (error) {
}
};
List.jsx
import React, { useState } from "react";
import { CircularProgress, Grid, Typography, InputLabel, MenuItem, FormControl, Select, ButtonGroup, Button } from "#material-ui/core";
import EventDetails from "../EventDetails/EventDetails"
import useStyles from "./styles"
const List = ({ events }) => {
const classes = useStyles();
const [type, setType] = useState("premierleague");
return (
<div className={classes.container}>
<FormControl className={classes.formControl}>
<InputLabel>Sport</InputLabel>
<Select value={type} onChange={(e) => setType(e.target.value)}>
<MenuItem value="premierleague">Premier League</MenuItem>
<MenuItem value="formula1">Formula 1</MenuItem>
</Select>
{/*<ButtonGroup value={type} onClick={(e) => setType(e.target.value)}>
<Button value="premierleague">Premier League</Button>
<Button value="formula1">Formula 1</Button>
</ButtonGroup>*/}
</FormControl>
<Grid container spacing={3} className={classes.list}>
{events?.map((event, i) => (
<Grid item key={i} xs={12}>
<EventDetails event={event} />
</Grid>
))}
</Grid>
</div>
)
}
export default List;
EventDetails.jsx
import React from "react";
const EventDetails = ({ event }) => {
console.log(event)
return (
<h3>{event.league}</h3>
)
}
export default EventDetails;
You're not sending the events to List component.
Try changing in App.js:
return (
<>
<CssBaseline />
<Header />
<List events={events} />
</>
)

Binding multipal row hang or crash web page using React with redux toolkit

https://codesandbox.io/s/large-data-array-with-redux-toolkit-forked-7tp63?file=/demo.tsx
In the given codesandbox example I am using react typescript with redux-toolkit
in codesandbox example. I am trying to bind countries with checkbox and textbox.
When I check on checkboxes it looks slow and textbox edit also feels very slow.
some time it breaks the page.
I am not sure what I am doing wrong.
You should make CountryItem it's own component and make it a pure component:
import React, { FC, useEffect } from "react";
import { createStyles, Theme, makeStyles } from "#material-ui/core/styles";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import { countryList } from "./dummyData";
import { Checkbox, Grid, TextField } from "#material-ui/core";
import { useAppDispatch, useAppSelector } from "./store/hooks";
import {
setCountries,
setCountrySelected,
setCountryValue
} from "./store/slice/geography-slice";
import { Country } from "./interface/country.modal";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
width: "100%",
backgroundColor: theme.palette.background.paper
}
})
);
const CountryItem: FC<{ country: Country }> = ({ country }) => {
console.log('render item',country.name)
const dispatch = useAppDispatch();
const handleCheckboxChange = (
event: React.ChangeEvent<HTMLInputElement>,
country: Country
) => {
const selectedCountry = { ...country, isSelected: event.target.checked };
dispatch(setCountrySelected(selectedCountry));
};
const handleTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const selectedCountry = { ...country, value: event.target.value };
dispatch(setCountryValue(selectedCountry));
};
return (
<ListItem button>
<Checkbox
checked={country?.isSelected ?? false}
onChange={(event) => {
handleCheckboxChange(event, country);
}}
/>
<ListItemText primary={country.name} />
<TextField value={country?.value ?? ""} onChange={handleTextChange} />
</ListItem>
);
};
const PureCountryItem = React.memo(CountryItem)
export default function SimpleList() {
const classes = useStyles();
const dispatch = useAppDispatch();
const { countries } = useAppSelector((state) => state.geography);
useEffect(() => {
dispatch(setCountries(countryList));
}, []);
return (
<div className={classes.root}>
<Grid container>
<Grid item xs={6}>
<List component="nav" aria-label="secondary mailbox folders">
{countries.map((country, index) => (
<PureCountryItem country={country} key={`CountryItem__${index}`} />
))}
</List>
</Grid>
</Grid>
</div>
);
}

Resources