I have a registration page created in ReactJS. One of the fields is a checkbox isadult. When I click on Register button and save the fields in a database (MongoDB), the value of isadult appears as [Object object] instead of a concrete value: True or False.
What am I doing wrong?
import React from 'react';
import { Paper, makeStyles, Grid, TextField, Button, Switch } from '#material-ui/core';
import config from '../../config/config.json';
import axios from 'axios';
const useStyles = makeStyles((theme) => ({
root: {
minWidth: '300px',
width: '50%',
padding: '20px 20px 20px 20px',
margin: 'auto'
}
}));
const Register = () => {
const classes = useStyles();
const [username, setUsername] = React.useState('');
const [password, setPassword] = React.useState('');
const [isadult, setIsAdult] = React.useState('');
const handleChangeIsAdult = (event) => {
setIsAdult({
...isadult,
[event.target.name]: event.target.value,
});
}
const handleRegister = () => {
if (username && password) {
const formData = new FormData();
formData.append('username', username);
formData.append('password', password);
formData.append('isadult', isadult);
axios.post(config.api.url + '/auth/register', formData)
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
}
}
return (
<Paper className={classes.root}>
<div >
<Grid container spacing={8} alignItems="flex-end">
<Grid item md={true} sm={true} xs={true}>
<TextField
id="username"
label="Username"
type="email"
fullWidth
value={username}
onChange={(e) => setUsername(e.target.value)}
autoFocus />
</Grid>
</Grid>
<Grid container spacing={8} alignItems="flex-end">
<Grid item md={true} sm={true} xs={true}>
<TextField
id="password"
label="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
fullWidth />
</Grid>
</Grid>
<Grid container spacing={8} alignItems="flex-end">
<Grid item md={true} sm={true} xs={true}>
<label>Is adult?</label>
<input
type='checkbox'
onChange={(event) => {
handleChangeIsAdult({
target: {
name: event.target.name,
value: event.target.checked,
},
});
}}
/>
</Grid>
</Grid>
<Grid container justify="center" style={{ marginTop: '10px' }}>
<Button
variant="outlined"
color="primary"
style={{ textTransform: "none" }}
onClick={handleRegister}
>
Register
</Button>
</Grid>
</div>
</Paper>
);
}
export default Register;
As your isadult state property is intended for use in the checkbox input, it needs to be declared and updated as a boolean.
You are declaring the propery as follows:
const [isadult, setIsAdult] = React.useState('');
The initial assignment is the empty string (''), which may cause issues with other places in your application code if they expect that property to be of type boolean. What you should do instead is start it off as a boolean:
const [isadult, setIsAdult] = React.useState(false);
Now, the main problem you are facing is the fact that the form data is being serialized with isadult being an object. This problem is coming from the fact that you are assigning an object to it through setIsAdult:
setIsAdult({
...isadult,
[event.target.name]: event.target.value,
});
The state property setter appends whatever value is passed to it directly to the property it is attached to. It works differently from setState that expects a state object. The right way to use the method in this case is:
setIsAdult(event.target.value);
Here, event.target.value contains exactly the checkbox checked boolean value that should go into isadult. Now, this property is serialized correctly as a boolean in your formData.
Related
This works but the when i console log the object it gives
{ "week":undefined, "name":undefined, "code":undefined }
Moreover does wrapping all the material ui component in form tag and treating the whole code as a form, is it appropriate?
here is my code:
const ExamSimulatorForm = () => {
const weekNumber = useRef();
const examSub = useRef();
const examCode = useRef();
const handleSubmit = (event) =>{
event.preventDefault()
const week = weekNumber.current.value
const subject = examSub.current.value
const code = examCode.current.value
const examSimulatorPayload = {
week:week,
subject:subject,
code:code
}
console.log(examSimulatorPayload)
}
const [code, setCode] = useState('Quiz');
const [examSubject, setExamSubject] = useState('');
const [field, setField] = useState(1)
const handleESubjectChange = (event) => {
setExamSubject(event.target.value);
};
const handleCode = (event) => {
setCode(event.target.value);
};
return (
<form >
<CardActions onSubmit={handleSubmit}>
<Grid container spacing={2} justifyContent='center' alignItems='center' direction='column'>
<Grid item>
<TextField
InputProps={{
inputProps: {
max: 12, min: 1
}
}}
label='Week'
type='number'
onChange={(event)=>setField(parseInt(event.target.value))}
style={{minWidth:250}}
ref = {weekNumber}
required
/>
</Grid>
<Grid item>
<FormControl style={{minWidth:250}}>
<InputLabel>Subject</InputLabel>
<Select
value={examSubject}
onChange={handleESubjectChange}
ref={examSub}
required
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item>
<FormControl style={{minWidth:250}}>
<InputLabel id="exam-code" >Exam Code</InputLabel>
<Select
labelId="exam-code"
id="exam-code-select"
value={code}
onChange={handleCode}
ref={examCode}
required
>
<MenuItem value={'Q'}>Q</MenuItem>
<MenuItem value={'M'}>M</MenuItem>
<MenuItem value={'F'}>F</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item>
<Button variant='contained' color='primary' style={{marginTop:94}} >Take Exam</Button>
</Grid>
</Grid>
</CardActions>
</form>
)
}
export default ExamSimulatorForm;
I have a similar form for attendance simulation, attendance dataset generation and exam dataset generation
I think this is good for you.
Please try this.
const ExamSimulatorForm = () => {
const [state, setState] = useState({
code: 'Quiz',
subject: '',
week: 1
});
const handleSubmit = (event) =>{
event.preventDefault()
const examSimulatorPayload = state;
console.log(examSimulatorPayload)
}
const handleChange = (evt, name) {
const { value } = evt.target;
setState({
...state,
[name]: value
});
}
return (
<form onSubmit={handleSubmit}>
<CardActions>
<Grid container spacing={2} justifyContent='center'
alignItems='center' direction='column'>
<Grid item>
<TextField
InputProps={{
inputProps: {
max: 12, min: 1
}
}}
label='Week'
type='number'
value={state.week}
onChange={(event)=>handleChange( event, "week")}
style={{minWidth:250}}
required
/>
</Grid>
<Grid item>
<FormControl style={{minWidth:250}}>
<InputLabel>Subject</InputLabel>
<Select
value={state.subject}
onChange={(event)=>handleChange( event, "subject")}
required
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item>
<FormControl style={{minWidth:250}}>
<InputLabel id="exam-code" >Exam Code</InputLabel>
<Select
labelId="exam-code"
id="exam-code-select"
value={state.code}
onChange={(event)=>handleChange( event, "code")}
required
>
<MenuItem value={'Q'}>Q</MenuItem>
<MenuItem value={'M'}>M</MenuItem>
<MenuItem value={'F'}>F</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid item>
<Button variant='contained' color='primary' style=
{{marginTop:94}} >Take Exam</Button>
</Grid>
</Grid>
</CardActions>
</form>
)
}
export default ExamSimulatorForm;
Please check it above code, and let me know your idea.
State updates in react are asynchronus, which means they don't occur as soon as you call them. You have to wait until the state updates to console.log because otherwise, nothing has changed and you are getting the initial value. You could do something like this:
useEffect(() => {
const subject = examSub.current.value
console.log(subject)
}, [examSubject])
useEffect() executes an action every time a state changes. Notice how at the end there is an array, with [examSubject]. This signifies the state that will trigger the effect. So when examSubject changes (when you assign a value to it), the effect will execute (in this case, it will log the subject).
When writing code, at least in my case, you don't need to worry about this. You can chnage the state and write your code as normal, but react might take a second or two to update the state. The only time I really notice this is when I console.log.
You can define the state for your inputs :
const [code, setCode] = React.useState('');
const [subject, setSubject] = React.useState('');
const [week, setWeek] = React.useState('');
const setExamCode = event => {
setCode(event.target.value);
};
const setSubject = event => {
setSubject(event.target.value);
};
const setWeek = event => {
setWeek(event.target.value);
};
Then call these methods from onChange event like : onChange={setExamCode} or {setSubject}
In case you want to handle data from single event and defining it's initial state try below approach :
const initialData = Object.freeze({
code: "",
subject: "",
week: ""
});
const [data, updateData] = React.useState(initialData );
const handleChange = (e) => {
updateData({
...data,
[e.target.name]: e.target.value
});
};
const handleSubmit = (e) => {
e.preventDefault()
console.log(data);
};
Then call these methods from onChange event like onChange={handleSubmit}
I had a form that has a lot of lag due to a large amount of state being handled for user's with a large number of job posts etc. I am trying to subdue this lag my switching my onChange to onBlur, this works great. The only problem is that my form no longer gets set to InitialState( empty string). I also have a submit button that I am keeping invalid until all inputs are filled. due to the onblur it remains invalid until I click away from the form. Is there a way I can still reset a form when using onBlur?? and does anyone have a solution to the issue of my button remaining invalid until I click away from the form. My inputs code are as follows:
the handleSubmit function:
const handleSubmit = async e => {
e.preventDefault()
setIsLoading(true)
const fireToken = await localStorage.FBIdToken
await axios
.post(`/job`, formData, {
headers: {
Authorization: `${fireToken}`
}
})
.then(res => {
setOpen(true)
setMessage(res.data)
fetchUser()
setIsLoading(false)
setIsModalOpen(false)
setFormData(INITIAL_STATE)
})
.catch(err => {
setErrors(err.response.data)
console.log(err)
setIsLoading(false)
})
}
The form code:
import React from 'react'
// components
import SelectStatus from './SelectStatus'
// Material UI Stuff
import CircularProgress from '#material-ui/core/CircularProgress'
import Typography from '#material-ui/core/Typography'
import TextField from '#material-ui/core/TextField'
import CardContent from '#material-ui/core/CardContent'
import Button from '#material-ui/core/Button'
import Card from '#material-ui/core/Card'
import Grid from '#material-ui/core/Grid'
// JobCardStyles
import useJobCardStyles from '../styles/JobCardStyles'
const NewJobForm = React.forwardRef(
({ handleSubmit, formData, handleInputChange, isloading }, ref) => {
const { company, position, status, link } = formData
const isInvalid = !company || !position || !link || !status || isloading
const classes = useJobCardStyles()
return (
<Card className={classes.card}>
<CardContent className={classes.content}>
<form noValidate onSubmit={handleSubmit} className={classes.form}>
<Grid
container
spacing={2}
alignItems="center"
justify="space-between"
>
<Grid item sm="auto" xs={12} className={classes.grid}>
<Typography>New</Typography>
<Typography>Job</Typography>
</Grid>
<Grid item sm={3} xs={12} className={classes.grid}>
<TextField
className={classes.jobField}
margin="normal"
fullWidth
id="company"
type="company"
label="Company"
name="company"
autoComplete="company"
defaultValue={company}
onBlur={handleInputChange('company')}
/>
</Grid>
<Grid item sm={3} xs={12} className={classes.grid}>
<TextField
className={classes.jobField}
margin="normal"
fullWidth
id="position"
type="position"
label="Position"
name="position"
autoComplete="position"
defaultValue={position}
onBlur={handleInputChange('position')}
/>
</Grid>
<Grid item sm={2} xs={12} className={classes.grid}>
<SelectStatus
status={status}
handleInputChange={handleInputChange}
/>
</Grid>
<Grid item sm={2} xs={12} className={classes.grid}>
<TextField
className={classes.jobField}
margin="normal"
fullWidth
id="link"
type="text"
label="Link"
name="link"
autoComplete="link"
defaultValue={link}
onBlur={handleInputChange('link')}
/>
</Grid>
<Grid item sm={1} xs={12} className={classes.grid}>
<Button
fullWidth
type="submit"
variant="contained"
color="primary"
disabled={isInvalid}
className={classes.submit}
disableElevation
>
Submit
{isloading && (
<CircularProgress size={30} className={classes.progress} />
)}
</Button>
</Grid>
</Grid>
</form>
</CardContent>
</Card>
)
}
)
export default NewJobForm
Try making another function to wrap several functions.
const NewJobForm = React.forwardRef(
//other logic
const reset = () => {//your reset function logic}
//ver 1
const handleOnBlur = (fn, relatedParam) => {
reset();
fn(relatedParam);
}
//ver 2
const handleOnBlur = (relatedParam) => {
reset();
handleInputChange(relatedParam);
}
return (
<TextField
//other props
onBlur={() => handleOnBlur('company')}
/>
)
Before I ask my question: Yes I did read every other article about this but im basicly just stuck and can't figure out how to fix this.
So basically my rendering and console.log are one step behind my useState changes. The biggest problem visually and functionally is the dynamic button which doesn't enable/disable correctly. My code is:
import React, { useState, useEffect } from 'react';
import ApiCalls from "../../classes/apiCallsClass"
import useStyles from "./styles";
import { TextField, Select, Paper, Grid, MenuItem, Button } from '#material-ui/core';
import { withStyles } from "#material-ui/styles";
export default function TicketCreation() {
const [buttonDisabled, setButtonDisabled] = useState(true);
const [, setTick] = useState(0);
const classes = useStyles();
const [ticketname, setTicketName] = useState('');
const [description, setDescription] = useState('');
const [prio, setPrio] = useState('');
const [system, setSystem] = useState('');
const handleChangePrio = event => {
setPrio(event.target.value);
checkFormFilled();
};
const handleChangeDescription = event => {
setDescription(event.target.value);
checkFormFilled();
};
const handleChangeName = event => {
setTicketName(event.target.value);
checkFormFilled();
};
const handleChangeSystem = event => {
setSystem(event.target.value);
checkFormFilled();
};
const checkFormFilled = () => {
if (ticketname && description && prio && system) {
setButtonDisabled(false, function(){
setTick(tick => tick + 1);
});
} else{
setButtonDisabled(true, function(){
setTick(tick => tick + 1);
});
}
}
const handelButtonSubmit = event => {
if (ApiCalls.createTicket(ticketname, system, prio, description)) {
console.log("Created");
}
}
return (
<div className={classes.root}>
<Grid container spacing={3}>
<Grid item xs={4}>
<Paper className={classes.paper}>
<TextField id="standard-basic" value={ticketname} onChange={handleChangeName} label="Ticketname" fullWidth />
</Paper>
</Grid>
<Grid item xs={4}>
<Paper className={classes.paper}>
<Select value={system} onChange={handleChangeSystem} displayEmpty fullWidth className={classes.selectEmpty}>
<MenuItem value="" disabled>
System
</MenuItem>
<MenuItem value={1}>Radar</MenuItem>
<MenuItem value={2}>Missles</MenuItem>
<MenuItem value={3}>Toilet</MenuItem>
</Select>
</Paper>
</Grid>
<Grid item xs={4}>
<Paper className={classes.paper}>
<Select value={prio} onChange={handleChangePrio} displayEmpty fullWidth className={classes.selectEmpty}>
<MenuItem value="" disabled>
Priority
</MenuItem>
<MenuItem value={1}>Low</MenuItem>
<MenuItem value={2}>Medium</MenuItem>
<MenuItem value={3}>High</MenuItem>
</Select>
</Paper>
</Grid>
<Grid item xs={12}>
<Paper className={classes.paper}>
<TextField
value={description}
onChange={handleChangeDescription}
id="outlined-multiline-static"
label="Ticket description"
multiline
rows="4"
variant="outlined"
fullWidth />
</Paper>
</Grid>
<Grid item xs={12}>
<Button disabled={buttonDisabled} onClick={handelButtonSubmit} variant="contained" color="primary">
Create
</Button>
</Grid>
</Grid>
</div>
);
}
Don't mind the setTick usestate, I tried this in combination with a callback to fix the issue but it wasn't working.
My question basically is:
What do I need to do to enable my button instantly without delay? (and maybe other things I do wrong? first time using react hooks)
State setters (setSystem, etc.) are asynchronous. That means where you're doing things like setSystem(event.target.value); checkFormFilled() -- the new value of system, in this case, may not be updated by the time checkFormFilled runs. That's likely why you're seeing things "lag behind" the values.
What you should be doing is not calling checkFormFilled directly after your set state calls; instead, put checkFormFilled in a useEffect hook that depends on the form elements. That way, React will call your checkFormFilled function when any of those values are actually updated.
useEffect(() => {
checkFormFilled();
}, [ticketname, description, prio, system]);
Then your change handlers only need to worry about setting the values, not calling the form filled check, and everything is guaranteed to be up to date.
Hello iI have a project in react + graphql + node and mongod, and iI tried to upload images to a folder in my app and i have a problem with the reload of app after the file save. I tried to manage a local state in the component when performing the upload process, but my local variable changed to the initial state because of a reload app, and cant it does not show a preview of my file. My goal is show a preview of img upload.
I think my problem is in the resolver, in the function storeUpload
Code.
API-----------Resolver------
const Product = require('../models/Product');
const { createWriteStream } = require("fs");
async function createProduct(root, args) {
console.log(args.file)
let product = await new Product(args)
console.log(product);
product.save()
if(args.file){
const { stream, filename, mimetype, encoding } = await args.file;
await storeUpload({ stream, product });
}
return product
}
const storeUpload = ({ stream, product }) =>
new Promise((resolve, reject) =>
stream
.pipe(createWriteStream("./images/" + product._id +".png"))
.on("finish", () => resolve())
.on("error", reject)
);
module.exports = {
createProduct
}
---React--- Component----
import React , { useState, useEffect } from 'react'
import {Grid} from '#material-ui/core'
import { Mutation } from "react-apollo"
import {PRODUCTOS} from './Productos'
import gql from "graphql-tag";
import {StyleStateConsumer} from '../../../Context/StylesStateContext'
/** #jsx jsx */
import { jsx, css } from '#emotion/core'
const borde = css({
borderStyle: 'solid'
})
const contendorForm = css({
borderRadius: 15,
backgroundColor: '#ffffe6'
},borde)
const itemsForm = css({
padding: 15
})
const inputStyle = css({
borderRadius: 5,
padding: 3
})
const ADD_PRODUCT = gql`
mutation CreateProduct($title: String, $price: Float, $file: Upload) {
createProduct(title: $title, price: $price, file: $file) {
_id
title
}
}
`;
const CrearProducto = props =>{
const [titleInput, settitleInput] = useState('')
const [priceInput, setpriceInput] = useState(0)
const [fileInput, setfileInput] = useState('')
const [imgName, setimgName] = useState('default')
const handleChange = e =>{
const {name, type, value} = e.target
if(name === 'titleInput'){settitleInput(value)}
if(name === 'priceInput'){setpriceInput(parseFloat(value))}
}
const imgUpdate = (imgConxt) => {
setimgName(imgConxt)
}
useEffect( imgUpdate )
const handleFile = (obj) =>{console.log(obj)}
return(
<StyleStateConsumer>
{({imagenID, updateHState})=>(
<Grid container css={borde}
direction="row"
justify="center"
alignItems="center">
<Grid item css={contendorForm}>
{imgUpdate(imagenID)}
<Grid container
direction="row"
justify="center"
alignItems="center"><Grid item >Crear Producto:</Grid>
</Grid>
<Grid container>
<Mutation mutation={ADD_PRODUCT} refetchQueries={[{
query: PRODUCTOS
}]}>
{(createProduct, { data, loading })=>{
if (loading) return <div>Loading...</div>
console.log(data)
return(
<div>
<form css={itemsForm}
onSubmit={ (e) => {
e.preventDefault()
createProduct({
variables: {
title: titleInput,
price: priceInput,
file: fileInput
}
})
//
updateHState(res.data.createProduct._id
=== undefined ? '' :
res.data.createProduct._id)
settitleInput('')
setpriceInput(0)
//
console.log(res.data.createProduct._id)
}}>
<Grid item>
<input
css={inputStyle}
type="file"
onChange={({
target: { validity, files:
[file] } }) => validity.valid &&
setfileInput(file)
}
/>
</Grid>
<Grid item>
<label>
Titulo:
</label>
</Grid>
<Grid item>
<input css={inputStyle} type="text"
name="titleInput"
placeholder="Ingresar TÃtulo"
value={titleInput}
onChange={handleChange}
/>
</Grid>
<Grid item>
<label>
Precio:
</label>
</Grid>
<Grid item>
<input css={inputStyle} type="number"
name="priceInput"
placeholder="Ingresar Precio"
value={priceInput}
onChange={handleChange}/>
</Grid>
<Grid item>
<input type="submit" name="Crear"/>
</Grid>
</form>
</div>
)
}}
</Mutation>
</Grid>
</Grid>
<Grid item css={[borde,
{
backgroundImage: `url('${require('../../../../../back-
end/images/' + imgName + '.png')}')`,
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
height: 300,
width: 300}]}>
</Grid>
</Grid>
)}
</StyleStateConsumer>
)
}
export default CrearProducto
Well, first the imgUpdate function you're passing to useEffect won't ever get its imgConxt parameter. This hook just calls an empty-parameter function to execute a bunch of code after render.
For your main problem, do you have any warnings or errors in the console ?
One quick but dirty fix would be to change your <input type='submit'> to type button and do your logic in an onClick handler there.
I am using react-validation to validate a login form. After submitting the form I want to know has an error or not. I can get the error state inside my custom button object in below way.
const customSubmit = ({ hasErrors, ...props }) => {
return <Button disabled={hasErrors} >Login</Button>
{hasErrors.toString()}/>
};
const SubmitButton = button(customSubmit);
Is there any way to get hasError state when submit a from?
I can share a similar code when I use State and handleChange to get the errors in the button.
First you need a function were you declare the useState.
import React from "react";
import { Box, Button, Collapse, Grid, TextField } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
function FormVal() {
const [phone, setPhone] = React.useState<string>();
const [errors, setErrors] = React.useState<{ phone: string }>();
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const {
target: { value },
} = event;
setErrors({ phone: "" });
setPhone(value);
let reg = new RegExp(/^\d*$/).test(value);
if (!reg) {
setErrors({ phone: "Only numbers" });
}
};
Now in the return section you need to declare the handleChange. There may be some elements that you don't know such as and that is because I am using Material-ui.
return (
<Box className={classes.root} width="100%" padding="5px">
<Grid container spacing={3}>
<Grid item xs={12}>
<TextField
id="outlined-basic"
autoComplete="off"
value={phone}
label="phone number"
inputProps={{ maxLength: 255 }}
onChange={handleChange}
required
error={Boolean(errors?.phone)}
helperText={errors?.phone}
variant="outlined"
/>
</Grid>
<Collapse in={phone?.length! > 0}>
<div style={{ width: "100%" }}>
<Button variant="contained">Save</Button>
</div>
</Collapse>
</Grid>
</Box>
);
}
export default FormVal;