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.
Related
I have a simple slider component from Material-UI v4, mostly taken from this example.
I want to update the UI, both the slider and the number (which is displayed in the UI) 'instantly', but debounce the dispatch to redux, so it doesn't update the store unnecessarily.
This is what I've done so far to modify the example:
import { debounce } from 'lodash';
import { useDispatch } from 'redux';
import { updatingAction } from 'action/foo';
export default function InputSlider() {
const dispatch = useDispatch();
const isReduxInSyncRef = useRef(false);
const reduxValue = useSelector(getReduxValue);
const [value, setValue] = React.useState(30);
const debouncedDispatch = debounce((value) => {
dispatch(updatingAction(value));
isReduxInSyncRef.current = true;
}, 200);
const updateStateAndRedux = (newValue) => {
setValue(newValue);
isReduxInSyncRef.current = false;
}
const handleSliderChange = (event, newValue) => {
updateStateAndRedux(newValue);
};
const handleInputChange = ({ target: { value } }) => {
updateStateAndRedux(value === '' ? '' : Number(value));
debouncedDispatch(value);
};
useEffect(() => {
if (reduxValue !== value && isReduxInSyncRef.current) {
setValue(reduxValue);
}
}, [value, reduxValue]);
return (
<div className={classes.root}>
<Typography id="input-slider" gutterBottom>
Volume
</Typography>
<Grid container spacing={2} alignItems="center">
<Grid item>
<VolumeUp />
</Grid>
<Grid item xs>
<Slider
value={typeof value === 'number' ? value : 0}
onChange={handleSliderChange}
onChangeCommitted={(event, value) => debouncedDispatch(value)}
aria-labelledby="input-slider"
/>
</Grid>
<Grid item>
<Input
className={classes.input}
value={value}
margin="dense"
onChange={handleInputChange}
inputProps={{
step: 10,
min: 0,
max: 100,
type: 'number',
'aria-labelledby': 'input-slider',
}}
/>
</Grid>
</Grid>
</div>
);
}
Because the slider and value need to move together, when I debounced the whole thing (redux + useState), I couldn't move the slider at all.
Using onChangeCommitted for the dispatch only helps the slider, but it seems like the number input onChange (handleInputChange) is firing the dispatch for every value, without being debounced.
What have I done wrong, and is there an easier/better way to do this?
I have an input field in child component. If you type in valid wallet address it should display wallet balance in parent component.
I don't know how to pass that e.target.value we are reading from input field.
I tried to do this function in parent component - Dashboard.
const [walletBalance, setWalletBalance] = useState('');
const getWalletBalance = async () => {
try {
const web3 = new Web3(Web3.givenProvider || 'ws://localhost:3000');
for (const wallet of dummyWallets) {
const balance = await web3.eth.getBalance(wallet.address);
console.log(balance);
const balanceConvertedFromWeiToEth = await Web3.utils.fromWei(balance, 'ether');
const shortenedBalance =
balanceConvertedFromWeiToEth.length >= 10
? balanceConvertedFromWeiToEth.slice(0, 10)
: balanceConvertedFromWeiToEth;
setWalletBalance(shortenedBalance);
}
} catch (error) {
console.error(error);
throw new Error('No wallet found');
}
};
render (
<p>Wallet balance: {walletBalance}</p>
and then pass this function down to child component with input field (need to prop drill 3 times for that) and in child component say:
const [inputText, setInputText] = useState('');
const handleInputChange = (e) => {
setInputText(e.target.value);
if (inputText === wallet.address) {
getWalletBalance()
}
};
render (
<input value={inputText} onChange={handleInputChange} />
)
It didn't work. No errors. Just nothing happens.
Then I thought that I need to do the other way around: Instead of trying to pass getWalletBalance() down as props, I need to read data from input field and pass that data to parent component, because parent needs to have access to the input field and read from it to be able to display wallet balance in parent.
I've also tried to create context:
import React, { useState, createContext } from 'react';
import { dummyWallets } from '../mocks/dummyWallets';
import Web3 from 'web3';
export const WalletContext = createContext();
// eslint-disable-next-line react/prop-types
export const TransactionProvider = ({ children }) => {
const [inputText, setInputText] = useState('');
const [walletBalance, setWalletBalance] = useState('');
const handleInputChange = async (e) => {
setInputText(e.target.value);
const web3 = new Web3(Web3.givenProvider || 'ws://localhost:3000');
for (const wallet of dummyWallets) {
if (inputText === wallet.address) {
const balance = await web3.eth.getBalance(wallet.address);
console.log(balance);
const balanceConvertedFromWeiToEth = await Web3.utils.fromWei(balance, 'ether');
const shortenedBalance =
balanceConvertedFromWeiToEth.length >= 10
? balanceConvertedFromWeiToEth.slice(0, 10)
: balanceConvertedFromWeiToEth;
setWalletBalance(shortenedBalance);
}
}
};
return (
<WalletContext.Provider
value={{ walletBalance, handleInputChange, inputText, setInputText,
setWalletBalance }}>
{children}
</WalletContext.Provider>
);
};
Then pass handleInputChange to onChange in child component and inputText as value, then use walletBalance in parent component.
All context things walletBalance, handleInputChange, inputText, setInputText and setWalletBalance threw undefined error.
Here is child component consuming data from context:
import React, { FC, useState, useContext } from 'react';
import { Formik, Form, Field } from 'formik';
import { Button, InputAdornment, Box, Grid } from '#material-ui/core';
import TextField from '#eyblockchain/ey-ui/core/TextField';
import { Search } from '#material-ui/icons';
import SvgEthereum from '../../../images/icon/Ethereum';
import { makeStyles } from '#material-ui/styles';
import { Theme } from '#material-ui/core';
import { dummyWallets } from '../../../shared/mocks/dummyWallets';
import Web3 from 'web3';
import { WalletContext } from '../../../shared/context/WalletContext';
const etherIconBorderColor = '#eaeaf2';
const useStyles = makeStyles((theme: Theme) => ({
etherIconGridStyle: {
padding: '16px 0 0 0'
},
etherIconStyle: {
fontSize: '220%',
textAlign: 'center',
color: theme.palette.primary.light,
borderTop: `solid 1px ${etherIconBorderColor}`,
borderBottom: `solid 1px ${etherIconBorderColor}`,
borderLeft: `solid 1px ${etherIconBorderColor}`,
padding: '5px 0 0 0',
[theme.breakpoints.down('sm')]: {
fontSize: '150%',
lineHeight: '43px'
}
},
buttonStyle: {
paddingTop: theme.spacing(2)
},
searchIconStyle: {
color: theme.palette.primary.light
}
}));
const onSubmitHandler = () => {
return;
};
const SearchInputForm: FC<any> = (props) => {
const { walletBalance, handleInputChange, inputText } = useContext(WalletContext);
const classes = useStyles();
return (
<Formik
onSubmit={onSubmitHandler}
initialValues={{ name: '' }}
render={({ values }) => {
return (
<Form>
<Grid container>
<Grid item xs={1} className={classes.etherIconGridStyle}>
<Box className={classes.etherIconStyle}>
<SvgEthereum />
</Box>
</Grid>
<Grid item xs={11}>
<Field
required
name="name"
placeholder={props.placeholdervalue}
value={inputText}
component={TextField}
onChange={handleInputChange}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<Search fontSize="large" className={classes.searchIconStyle} />
</InputAdornment>
)
}}
/>
</Grid>
<Grid item xs={12}>
<Box
display="flex"
justifyContent="flex-end"
alignItems="flex-end"
className={classes.buttonStyle}>
<Button type="submit" variant="contained" color="secondary" size="large" disabled>
Create Alert
</Button>
</Box>
</Grid>
</Grid>
</Form>
);
}}
/>
);
};
export default SearchInputForm;
Here is parent component where I pull walletBalance from context:
const Dashboard: FC<any> = () => {
const { walletBalance } = useContext(WalletContext);
const classes = useStyles();
return (
<>
<Container maxWidth={false} className={classes.dashboardContainer}>
<Grid>
<Box data-testid="alert-page-heading">
<Typography variant="h3">Alerts</Typography>
</Box>
<Box data-testid="alert-page-subheading">
<Typography className={classes.subHeadingStyle}>
Monitor the Web 3.0 for interesting blockchain events
</Typography>
</Box>
</Grid>
<Grid container spacing={4}>
<Grid item xs={12} sm={8}>
<Box className={classes.dashboardCard}>
<Box className={classes.searchBox}>
<SearchTabs />
</Box>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between'
}}>
<Box
sx={{
height: '60%',
padding: '1%',
fontSize: 20
}}>
<a href="http://localhost:3000/" className={classes.backToAlertsLink}>
Back to my alerts
</a>
<p className={classes.boldText}>Recent transactions</p>
</Box>
<Box sx={{ height: '60%', padding: '4%', fontSize: 20 }}>
<span>Wallet Balance</span>
<span className={classes.balanceNumber}>{walletBalance} ETH</span>
</Box>
</Box>
</Box>
</Grid>
<Grid item xs={12} sm={4}>
<Box className={classes.dashboardCard}>
<Box sx={{ height: '60%', padding: '2%', fontSize: 20, fontWeight: 'bold' }}>
Top 10 alerts
</Box>
</Box>
</Grid>
</Grid>
</Container>
</>
);
};
export default Dashboard;
The problem is that I pull handleChange from context and apply to input field in child component to read the value from the input and then pull walletBalance from context in parent but that walletBalance is not updated (doesn't contain input value in it).
Can someone please share knowledge on how to read data from child component input field and display something from that in parent component?
Any advice is appreciated. Thank you.
It was working a while ago, the searchBar component was a class component and I tried to change it to a functional component but suddenly I receive error 400. I tried to create a new youtube API but it keeps showing.
is it because of api?
Here is my App.js
import { Grid } from '#material-ui/core'
import React, { useState } from 'react'
import youtube from './api/youtube'
import { SearchBar, VideoDetail, VideoList } from './components'
const App = () => {
const [videos, setVideo] = useState([])
const [selectdvideo, setSelectedVideo] = useState(null)
const onFormSubmit = async (value) => {
const response = await youtube.get('search', {
params: {
part: 'snippet',
maxResults: 5,
key: '',
q: value,
},
})
console.log('hello')
setVideo(response.data.items)
setSelectedVideo(response.data.items[0])
}
return (
<Grid justifyContent="center" container spacing={10}>
<Grid item xs={12}>
<Grid container spacing={10}>
<Grid item xs={12}>
<SearchBar onFormSubmit={onFormSubmit} />
</Grid>
<Grid item xs={8}>
<VideoDetail video={selectdvideo} />
</Grid>
<Grid item xs={4}>
<VideoList videos={videos} onSelectVideo={setSelectedVideo} />
</Grid>
</Grid>
</Grid>
</Grid>
)
}
export default App
This is the searchBar
import { Paper, TextField } from '#material-ui/core'
import React, { useState } from 'react'
const SearchBar = ({ onFormSubmit }) => {
const [searchTerm, setSearchTerm] = useState('')
const handleChange = (event) => setSearchTerm(event.target.value)
const onKeyPress = (event) => {
if (event.key === 'Enter') {
onFormSubmit(searchTerm)
}
}
return (
<Paper elavtion={6} style={{ padding: '25px' }}>
<TextField
fullWidth
label="Search..."
value={searchTerm}
onChange={handleChange}
onKeyPress={onKeyPress}
/>
</Paper>
)
}
export default SearchBar
[enter image description here][1]
I receive this error
https://ibb.co/Vpz9nKG
I am new to react and I am making a simple todo app using react js and material ui. I have separate components to take in user input (TodoInput.js) which sends props to a component that renders individual todo tasks and displays a checkbox (TodoCards.js). What I want to do is display the total number of completed tasks onto the page which is updated when the user completes a todo by checking a checkbox. To achieve this, I have an array that stores all the user's completed tasks. At the moment whenever a checkbox is checked, all tasks are added to this array. I ran into a problem where I am unsure of how to only push values into this new array when the checkbox of that specific task is checked. Any guidance or explanations towards the right direction is greatly appreciated.
TodoInput.js
import React, { useState } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import { TextField, Button } from '#material-ui/core';
import { TodoCards } from '../UI/TodoCards';
import { Progress } from '../UI/Progress';
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
width: '25ch',
textAlign: 'center'
},
},
}));
export default function TodoInput() {
const classes = useStyles();
const [userInput, setUserInput] = useState({
id: '',
task: ''
});
const [todos, setTodos] = useState([])
//state for error
const [error, setError] = useState({
errorMessage: '',
error: false
})
//add the user todo with the button
const submitUserInput = (e) => {
e.preventDefault();
//add the user input to array
//task is undefined
if (userInput.task === "") {
//render visual warning for text input
setError({ errorMessage: 'Cannot be blank', error: true })
console.log('null')
} else {
setTodos([...todos, userInput])
console.log(todos)
setError({ errorMessage: '', error: false })
}
console.log(loadedTodos)
}
//set the todo card to the user input
const handleUserInput = function (e) {
//make a new todo object
setUserInput({
...userInput,
id: Math.random() * 100,
task: e.target.value
})
//setUserInput(e.target.value)
//console.log(userInput)
}
const loadedTodos = [];
for (const key in todos) {
loadedTodos.push({
id: Math.random() * 100,
taskName: todos[key].task
})
}
return (
<div>
<Progress taskCount={loadedTodos.length} />
<form className={classes.root} noValidate autoComplete="off" onSubmit={submitUserInput}>
{error.error ? <TextField id="outlined-error-helper-text" label="Today's task" variant="outlined" type="text" onChange={handleUserInput} error={error.error} helperText={error.errorMessage} />
: <TextField id="outlined-basic" label="Today's task" variant="outlined" type="text" onChange={handleUserInput} />}
<Button variant="contained" color="primary" type="submit">Submit</Button>
{userInput && <TodoCards taskValue={todos} />}
</form>
</div>
);
}
TodoCards.js
import React, { useState } from 'react'
import { Card, CardContent, Typography, FormControlLabel, Checkbox } from '#material-ui/core';
import { CompletedTasks } from './CompletedTasks';
export const TodoCards = ({ taskValue }) => {
const [checked, setChecked] = useState(false);
//if checked, add the task value to the completed task array
const completedTasks = [];
const handleChecked = (e) => {
setChecked(e.target.checked)
for (const key in taskValue) {
completedTasks.push(taskValue[key])
}
console.log(completedTasks.length)
}
return (
< div >
<CompletedTasks completed={completedTasks.length} />
<Card>
{taskValue.map((individual, i) => {
return (
<CardContent key={i}>
<Typography variant="body1">
<FormControlLabel
control={
<Checkbox
color="primary"
checked={checked[i]}
onClick={handleChecked}
/>
}
label={individual.task} />
</Typography>
</CardContent>
)
})}
</Card>
</div >
)
}
CompletedTasks.js (displays the total number of completed tasks)
import React from 'react'
import InsertEmoticonOutlinedIcon from '#material-ui/icons/InsertEmoticonOutlined';
import { Typography } from '#material-ui/core';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
paper: {
padding: theme.spacing(2),
marginTop: '20px',
textAlign: 'center',
color: theme.palette.text.secondary,
},
}));
export const CompletedTasks = ({ completed }) => {
const classes = useStyles();
return (
<div className={classes.root}>
<InsertEmoticonOutlinedIcon fontSize="large" />
<Typography variant="h6">
Completed tasks:{completed}
</Typography>
</div>
)
}
One issue I see here is that you start with a boolean type checked state in TodoCards, and only ever store a single boolean value of the last checkbox interacted with. There's no way to get a count or to track what's previously been checked.
Use an object to hold the completed "done" checked values, then count the number of values that are checked (i.e. true) after each state update and rerender. Use the task's id as the key in the checked state.
export const TodoCards = ({ taskValue = [] }) => {
const [checked, setChecked] = useState({});
const handleChecked = id => e => {
const { checked } = e.target;
setChecked((values) => ({
...values,
[id]: checked
}));
};
return (
<div>
<CompletedTasks
completed={Object.values(checked).filter(Boolean).length}
/>
<Card>
{taskValue.map(({ id, task }) => {
return (
<CardContent key={id}>
<Typography variant="body1">
<FormControlLabel
control={
<Checkbox
color="primary"
checked={checked[id]}
onClick={handleChecked(id)}
/>
}
label={task}
/>
</Typography>
</CardContent>
)
})}
</Card>
</div >
)
}
To do this, you first need to only push the checked key to your array:
First send your key to the eventHandler:
{taskValue.map((individual, i) => {
return (
<CardContent key={i}>
<Typography variant="body1">
<FormControlLabel
control={
<Checkbox
color="primary"
checked={checked[i]}
onClick={() => handleChecked(individual)}
/>
}
label={individual.task} />
</Typography>
</CardContent>
)
})}
Then in your Handler, push it into the array:
const handleChecked = (key) => {
//setChecked(e.target.checked)
completedTasks.push(key)
console.log(completedTasks.length)
}
BUT
Because you are not modifying any state so the changes won't be updated to the UI, you need to use a state to store your completedTasks.
const [completedTasks, setCompletedTasks] = useState([]);
const handleChecked = (key) => {
setCompletedTasks([...completedTasks, key])
}
Please note that this is only a guide so you can get to the right way, not a complete working example
In the hopes that someone else may find this useful, I was able to come up with a solution thanks to the suggestions given. Below is the updated code for the TodoCards.js component:
import React, { useState } from 'react'
import { Card, CardContent, Typography, FormControlLabel, Checkbox } from '#material-ui/core';
import { CompletedTasks } from './CompletedTasks';
export const TodoCards = ({ taskValue }) => {
const [checked, setChecked] = useState(false);
//if checked, add the task value to the completed task array
const [completedTasks, setCompletedTasks] = useState([]);
const handleChecked = key => {
setCompletedTasks([...completedTasks, key])
completedTasks.push(key)
console.log(completedTasks.length)
setChecked(true)
};
if (taskValue.length === completedTasks.length) {
console.log('all tasks complete')
}
return (
<div>
<CompletedTasks completed={completedTasks.length} />
<Card>
{taskValue.map((individual, i) => {
return (
<CardContent key={i}>
<Typography variant="body1">
<FormControlLabel
control={
<Checkbox
color="primary"
checked={checked[i]}
onClick={() => handleChecked(individual)}
/>
}
label={individual.task}
/>
</Typography>
</CardContent>
)
})}
</Card>
</div >
)
}
Only the checked todo items are pushed into the new array (completedTasks) and this is updated using useState.
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.