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
Related
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
;
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>
);
}
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 ! :)
I have an action to login user using axios post request. but the action function is not being called on clicking login button in my functional component.
here is the action function
import {
SET_USER,
SET_ERRORS,
CLEAR_ERRORS,
LOADING_UI,
SET_AUTHENTICATED,
SET_UNAUTHENTICATED
} from "../types";
import axios from "axios";
export const loginUser = (userData) => (dispatch) => {
dispatch({ type: LOADING_UI });
axios
.post("/api/auth/login", userData)
.then((res) => {
const FBIdToken = `Bearer ${res.data.token}`;
localStorage.setItem("FBIdToken", FBIdToken);
axios.defaults.headers.common["Authorization"] = FBIdToken;
dispatch(getUserData());
dispatch({ type: CLEAR_ERRORS });
window.location = "/";
})
.catch((err) => {
dispatch({
type: SET_ERRORS,
payload: err.response.data
});
});
};
export const getUserData = () => (dispatch) => {
axios
.get("/api/user")
.then((res) => {
dispatch({
type: SET_USER,
payload: res.data
});
})
.catch((err) => console.log(err));
};
and here is my functional component.
i have logged statements before and after the function call, both are being printed but the function is not being executed
import React, { useState } from "react";
import PropTypes from "prop-types";
import AppIcon from "../images/icon.png";
import axios from "axios";
import { Link } from "react-router-dom";
//MUI imports
import withStyles from "#material-ui/core/styles/withStyles";
import Grid from "#material-ui/core/Grid";
import Typography from "#material-ui/core/Typography";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import CircularProgress from "#material-ui/core/CircularProgress";
//Redux
import { connect } from "react-redux";
import { loginUser } from "../Redux/actions/userActions";
const styles = {
form: {
textAlign: "center"
},
image: {
margin: "20px auto 20px auto"
},
pageTitle: {
margin: "10px auto"
},
textField: {
margin: "10px auto"
},
button: {
marginTop: 20,
position: "relative"
},
customError: {
color: "red"
},
progress: {
position: "absolute"
}
};
function Login({ classes, UI: { loading } }) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errors, setErrors] = useState({});
const handleSubmit = (e) => {
e.preventDefault();
const userData = {
email: email,
password: password
};
console.log("before");
loginUser(userData);
console.log("after");
};
return (
<Grid container className={classes.form}>
<Grid item sm />
<Grid item sm>
<img src={AppIcon} alt="app logo" className={classes.image} />
<Typography variant="h2" className={classes.pageTitle}>
Login
</Typography>
<form noValidate onSubmit={handleSubmit}>
<TextField
id="email"
name="Email"
type="email"
label="Email"
className={classes.textField}
helperText={errors.email}
error={errors.email ? true : false}
value={email}
onChange={(e) => setEmail(e.target.value)}
fullWidth
/>
<TextField
id="password"
name="password"
type="password"
label="Password"
className={classes.textField}
helperText={errors.password}
error={errors.password ? true : false}
value={password}
onChange={(e) => setPassword(e.target.value)}
fullWidth
/>
{errors.error && (
<Typography variant="body2" className={classes.customError}>
Wrong credentials, please try again.
</Typography>
)}
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
disabled={loading}
>
Login
{loading && (
<CircularProgress size={30} className={classes.progress} />
)}
</Button>
<br />
<small>
don't have an account? Signup <Link to="/signup">here</Link>
</small>
</form>
</Grid>
<Grid item sm />
</Grid>
);
}
Login.propTypes = {
classes: PropTypes.object.isRequired,
loginUser: PropTypes.func.isRequired,
user: PropTypes.object.isRequired,
UI: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
user: state.user,
UI: state.UI
});
const mapActionsToProps = {
loginUser
};
export default connect(
mapStateToProps,
mapActionsToProps
)(withStyles(styles)(Login));
You should execute the loginUser passed via props, not the one you import at the top of the file, better give it a different name to avoid confusion.
e.g.
function Login({ classes, UI: { loading }, loginUser }) {
I am fetching array of objects from an API endpoint,
I am using react-redux for state management.
I have two problems.
data gets fetched but only loads and displays after I refresh the page.
console logging single statement 4 times 2 blank array before setting state and 2 with data after setting state where it should have been only once I guess.
Following is my code.
Dashboard Action
import dashboardAPI from "../../../API/ShopUserAPI/dashboardAPI"
export const CURRENT_ORDER_LOAD = "CURRENT_ORDER_LOAD"
export const CURRENT_ORDER_FETCH = "CURRENT_ORDER_FETCH"
export const CURRENT_ORDER_ERROR = "CURRENT_ORDER_ERROR"
export const currentOrderAction = () => {
return dispatch => {
dispatch({ type: CURRENT_ORDER_LOAD, payload: '' })
dashboardAPI.get('/orders')
.then(resp => {
console.log(resp)
dispatch({ type: CURRENT_ORDER_FETCH, payload: resp.data.data.orders })
})
.catch(err => {
console.log(err)
dispatch({ type: CURRENT_ORDER_ERROR, payload: 'error occured. Retry !' })
})
}
}
Dashboard Reducer
import { CURRENT_ORDER_LOAD, CURRENT_ORDER_FETCH, CURRENT_ORDER_ERROR } from "../actions/dashboardActions";
const initialState = {
isLoading: true,
currentOrderData: [],
currentOrdererror: ''
}
const dashboardReducer = (state = initialState, action) => {
switch (action.type) {
case CURRENT_ORDER_LOAD:
return {
...state,
isLoading: true
}
case CURRENT_ORDER_FETCH:
return {
...state,
isLoading: false,
currentOrderData: action.payload
}
case CURRENT_ORDER_ERROR:
return {
...state,
isLoading: false,
currentOrdererror: action.payload
}
default:
return state
}
}
export default dashboardReducer
Dashboard page
import React from 'react'
import { Link } from 'react-router-dom'
import { makeStyles } from '#material-ui/core/styles';
import { Button, Typography, Grid, DialogContent, DialogActions, Dialog, DialogTitle, Divider, ExpansionPanel, ExpansionPanelSummary, ExpansionPanelDetails } from "#material-ui/core";
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import { connect } from "react-redux";
const useStyles = makeStyles((theme) => ({
contentCenter: {
display: 'flex',
justifyContent: 'center',
marginTop: '1rem'
},
newSection: {
marginTop: '1rem'
},
innerSection: {
padding: '1rem'
},
heading: {
fontSize: theme.typography.pxToRem(15),
color: theme.palette.text.secondary,
},
secondaryHeading: {
fontSize: theme.typography.pxToRem(15),
color: theme.palette.text.secondary,
},
}))
const CurrentOrders = (props) => {
console.log(props.currentOrderData) // console.log printing 4 times
const [expanded, setExpanded] = React.useState(false);
const handleChange = (panel) => (event, isExpanded) => {
setExpanded(isExpanded ? panel : false);
};
const classes = useStyles()
return (
<div className={classes.newSection}>
{/* current orders list */}
<Typography className={classes.contentCenter}>
Current orders
</Typography>
{/* Orders expansion panel list */}
{props.currentOrderData.map(currentOrders => (
<ExpansionPanel expanded={expanded === 'panel1'} onChange={handleChange('panel1')} className={classes.newSection}>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1bh-content"
id="panel1bh-header"
>
<Typography className={classes.heading}>Order ID: #{currentOrders.orderID}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Grid container justify="space-between">
<Grid item>
<Link to="/order" style={{ textDecoration: "none" }}>
<Button size="small" variant="contained" color="primary">
View Order
</Button>
</Link>
</Grid>
</Grid>
</ExpansionPanelDetails>
</ExpansionPanel>
))}
</div>
)
}
const mapStateToProps = state => {
return {
currentOrderData: state.dashboard.currentOrderData
}
}
export default connect(mapStateToProps)(CurrentOrders)
I've dispatched dashboard action in the parent component of this page
DIspatching of Dashboard
import React, { useEffect } from "react";
import { Container } from "#material-ui/core";
import CurrentOrders from "./CurrentOrders";
import TodaysOrders from "./TodaysOrders";
import { makeStyles } from '#material-ui/core/styles';
import { Button, Typography, Grid, DialogContent, DialogActions, Dialog, DialogTitle, Divider } from "#material-ui/core";
import { connect } from "react-redux";
import { currentOrderAction } from "../../../store/ShopUserStore/actions/dashboardActions";
const useStyles = makeStyles((theme) => ({
contentCenter: {
display: 'flex',
justifyContent: 'center',
marginTop: '1rem'
},
newSection: {
marginTop: '1rem'
},
innerSection: {
padding: '1rem'
},
buttonSuccess: {
color: theme.palette.success.main
}
}))
const Home = (props) => {
const [open, setOpen] = React.useState(false);
useEffect(() => {
props.getCurrentOrders()
})
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const classes = useStyles()
return (
<div>
<Container>
{/* shop status */}
<Grid container justify="flex-start" className={classes.newSection}>
<Button variant="outlined" color="primary" onClick={handleClickOpen} className={classes.buttonSuccess}>
Online
</Button>
</Grid>
{/* shop status confirmation dialog */}
<Dialog onClose={handleClose} aria-labelledby="customized-dialog-title" open={open}>
<DialogTitle id="customized-dialog-title" onClose={handleClose}>
Shop status
</DialogTitle>
<Divider />
<DialogContent>
<Typography className={classes.newSection}>
Are you sure that you want to change shop status ?
</Typography>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="primary">
No
</Button>
<Button autoFocus onClick={handleClose} color="primary">
Yes
</Button>
</DialogActions>
</Dialog>
<Grid container spacing={3}
>
<Grid item xs={12} md={6}>
<CurrentOrders />
</Grid>
<Grid item xs={12} md={6}>
<TodaysOrders />
</Grid>
</Grid>
</Container>
</div>
)
}
const mapDispatchToProps = (dispatch) => {
return {
getCurrentOrders: () => dispatch(currentOrderAction())
}
}
export default connect(null, mapDispatchToProps)(Home)
When you run your application, you should stumble into a nasty loop. The effect hook runs when the component mounts but also when the component updates. Because we are setting the state after every data fetch, the component updates and the effect runs again. It fetches the data again and again. That's a bug and needs to be avoided. We only want to fetch data when the component mounts. That's why you can provide an empty array as second argument to the effect hook to avoid activating it on component updates but only for the mounting of the component.
Try this:
useEffect(async () => {
props.getCurrentOrders()
}, []);