How to send data to Final Form - reactjs

The nearest example of what I am trying to do is this
https://codesandbox.io/s/3x989zl866?file=/src/index.js
However instead of fields that people fill out, I have 3 buttons with prices: 300,500,1000
When they click on them I need to to set the value in
<TextField
name="campaignAmount"
id="campaignAmount"
component="input"
type="number"
placeholder="Total"
/>
I can set the field but it won't send the value to the final form.
My full code:
import React, { useState } from 'react';
import Wizard from '../wrappers/Wizard'
import { Form, Field } from 'react-final-form'
import ExternalModificationDetector from '../wrappers/ExternalModificationDetector';
import BooleanDecay from '../wrappers/BooleanDecay'
import {
Typography,
Paper,
Grid,
Button,
ButtonGroup,
Box,
CssBaseline,
} from '#material-ui/core';
import DateFnsUtils from '#date-io/date-fns';
import {
TextField,
Radios,
KeyboardDatePicker,
} from 'mui-rff';
const onSubmit = async values => {
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
// MAIN FORM AREA
export default function BuildAdStepper(props: MyFormProps) {
// const [selectedDate, handleDateChange] = useState(new Date());
const [txtValue, setTxtValue] = useState({})
const today = new Date();
const dateTimeFormat = new Intl.DateTimeFormat('en', { year: 'numeric', month: '2-digit', day: '2-digit' })
const [{ value: month },,{ value: day },,{ value: year }] = dateTimeFormat.formatToParts(today)
const nmonth = new Date(today.getFullYear(), today.getMonth()+1, day);
const [{ value: nextmonth },,{ value: nextday },,{ value: nextyear }] = dateTimeFormat.formatToParts(nmonth)
function schemaTypeSelectionHandle(event) {
console.log('key: ', event.target.parentNode.value);
setTxtValue(""+event.target.parentNode.value);
// console.log('key: ', event.target.attributes.getNamedItem('data-key').value);
}
const calculator = createDecorator({
field: /day\[\d\]/, // when a field matching this pattern changes...
updates: {
// ...update the total to the result of this function
total: (ignoredValue, allValues) =>
(allValues.day || []).reduce((sum, value) => sum + Number(value || 0), 0)
}
})
return (
<div style={{ padding: 16, margin: 'auto', maxWidth: 600 }}>
<CssBaseline />
<Typography variant="h5" align="center" component="h2" gutterBottom>
Create your campaign
</Typography>
<Paper style={{ padding: 16 }}>
<Form
onSubmit={onSubmit}
decorators={[calculator]}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<Wizard
initialValues={{promoting:"business", startDate:`${day}-${month}-${year }`,endDate:`${nextday}-${nextmonth}-${nextyear }`}}
onSubmit={onSubmit}
>
<Wizard.Page>
<Typography variant="h5">What are you Advertising</Typography>
<Box m={2}/>
<Radios
name="promoting"
formControlProps={{ margin: 'none' }}
radioGroupProps={{ row: true }}
data={[
{ label: 'Business', value: 'business' },
{ label: 'Community Project', value: 'community' },
{ label: 'Event', value: 'event' },
]}
/>
<Box m={2}/>
</Wizard.Page>
<Wizard.Page>
<Typography variant="h5">Name Your Campaign</Typography>
<Box m={2}/>
<TextField
name="campaignName"
margin="none"
fullWidth
variant="outlined"
required={true}
/>
<Box m={2}/>
</Wizard.Page>
<Wizard.Page>
<Typography variant="h5">Set your schedule and budget</Typography>
<Box m={2} />
<KeyboardDatePicker label="Start Date" format="dd-MM-yyyy" name="startDate" dateFunsUtils={DateFnsUtils} required={true} value={new Date()} />
<Box m={2} />
<KeyboardDatePicker label="End Date" format="dd-MM-yyyy" name="endDate" dateFunsUtils={DateFnsUtils} value={new Date(nextyear,nextmonth - 1,nextday)} required={true}/>
<Box m={2} />
<Typography variant="h5">Your budget will be evenly distributed between start and end dates.</Typography>
<Grid container>
<Grid item lg={6}>
<ButtonGroup color="primary" onClick={schemaTypeSelectionHandle.bind(this)} aria-label="outlined primary button group">
<Button value='300'>$300</Button>
<Button value='500'>$500</Button>
<Button value='1000'>$1000</Button>
</ButtonGroup>
</Grid>
<Grid item lg={3}>
<ExternalModificationDetector name="total">
{externallyModified => (
<BooleanDecay value={externallyModified} delay={1000}>
{highlight => (
<TextField
name="campaignAmount"
id="campaignAmount"
component="input"
type="number"
placeholder="Total"
style={{
transition: 'background 500ms ease-in-out',
background: highlight ? 'yellow' : 'none'
}}
/>
)}
</BooleanDecay>
)}
</ExternalModificationDetector>
</Grid>
</Grid>
</Wizard.Page>
</Wizard>
)}
/>
</Paper>
</div>
);
}
I have uploaded the code to codesandbox.io
But can't seem to run it.
https://codesandbox.io/s/keen-architecture-iu02l?file=/src/core/pages/BuildAd.js

If you want to set the field value using multiple buttons, you can do the following:
Add an artificial field calling, say, budget:
<Field
name="budget"
render={({ input }) => (
<button
onClick={() => input.onChange(500)}
type="button"
>
500
</button>
)}
/>
This field renders a button, which has an onClick handler that calls the field's onChange method and sets the appropriate value.
Then, if you duplicate this field like following:
<Field name="budget" render={({ input }) => (
<button onClick={() => input.onChange(500)} type="button">500</button>)}
/>
<Field name="budget" render={({ input }) => (
<button onClick={() => input.onChange(1000)} type="button">1000</button>)}
/>
<Field name="budget" render={({ input }) => (
<button onClick={() => input.onChange(10000)} type="button">10000</button>)}
/>
you would have three buttons that update the budget to 500, 1000 and 10000 accordingly.
Since you have this value in the form state, you can copy it to the campaignAmount field using a final-form-calculate decorator:
const calculator = createDecorator({
field: "budget",
updates: {
total: (_, allValues) => allValues.budget
}
});
Here is a complete codesandbox: https://codesandbox.io/s/httpsstackoverflowcomquestions63323056how-to-send-data-to-final-form-ssoz3?file=/index.js.

Related

Why do I have to refresh my page to see my Card rendered?

I have a React+ Rails app .I am facing a small prob with my react application. Whenever I make a post request I am navigated to a page,but I cannot see my card rendered.After I refresh the page I can see the card getting rendered AND it ever persists on the page .Why do I have to refresh the page though?How to solve this?
Here is my code.I have added some MUI designing pardon me if thats confusing
Reservation Form
function ReservationForm() {
const navigate = useNavigate();
const params = useParams();
const { user,errors,setErrors } = useContext(Cont);
const [reservationData, setReservationData] = useState({
name: "",
date: "",
time: "",
num: "",
contact: "",
occasion: "",
});
function handleReservationChange(event) {
setReservationData({
...reservationData,
[event.target.name]: event.target.value,
});
}
function handleReservationChangeWithNameAndValue(name, newValue) {
setReservationData({
...reservationData,
[name]: newValue,
});
}
function handleReservationSubmit(event) {
event.preventDefault();
const newReservation = {
...reservationData,
restaurant_id: params.id,
user_id: user.id,
};
fetch(`/reservations`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(newReservation),
})
.then((r) => r.json())
.then(
setReservationData({
name: "",
date: "",
time: "",
num: "",
contact: "",
occasion: "",
})
);
navigate("/myreservations");
}
return (
<>
<div className="overlay3">
<Box
component="form"
sx={{
"& > :not(style)": { m: 1 },
}}
noValidate
autoComplete="off"
onSubmit={handleReservationSubmit}
>
<h1 className="editheadings">Reserve 🍽️</h1>
<FormControl className="inputstyle">
<InputLabel htmlFor="component-outlined">Name</InputLabel>
<OutlinedInput
type="text"
name="name"
id="name"
value={reservationData.name}
onChange={handleReservationChange}
label="Name"
/>
</FormControl>
<br />
<FormControl name="date" className="inputstyle">
<LocalizationProvider
dateAdapter={AdapterDayjs}
name="date"
fullWidth
>
<DatePicker
label="Date"
name="date"
value={reservationData.date || null}
onChange={(newVal) =>
handleReservationChangeWithNameAndValue("date", newVal)
}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
</FormControl>
<FormControl className="inputstyle">
<LocalizationProvider dateAdapter={AdapterDayjs}>
<TimePicker
name="time"
label="Time"
value={reservationData.time || null}
onChange={(newVal) =>
handleReservationChangeWithNameAndValue("time", newVal)
}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
</FormControl>
<br />
<FormControl className="inputstyle">
<InputLabel htmlFor="component-outlined">No. of Guests</InputLabel>
<OutlinedInput
type="number"
name="num"
value={reservationData.num}
onChange={handleReservationChange}
/>
</FormControl>
<br />
<FormControl className="inputstyle">
<InputLabel htmlFor="component-outlined">Contact</InputLabel>
<OutlinedInput
type="tel"
name="contact"
value={reservationData.contact}
onChange={handleReservationChange}
placeholder="contact"
/>
</FormControl>
<br />
<FormControl className="inputstyle">
<InputLabel htmlFor="component-outlined">Occasion</InputLabel>
<OutlinedInput
type="text"
name="occasion"
value={reservationData.occasion}
onChange={handleReservationChange}
/>
</FormControl>
<Stack paddingLeft={15} direction="row" id="loginbutton">
<ColorButton variant="contained" type="submit">
{" "}
Reserve
</ColorButton>
</Stack>
</Box>
</div>
</>
);
}
export default ReservationForm;
My Reservations
import {useEffect, useState } from "react";
import ReservationCard from "./ReservationCard";
import { useContext } from "react";
import { Cont } from "../Cont";
function MyReservations(){
const {reservations,setReservations}=useContext(Cont);
useEffect(()=>{
fetch("/reservations")
.then(res=>res.json())
.then(reservationData=>{
setReservations(reservationData)
})
},[])
function handleUpdateReservation(updatedReservation) {
const updatedReservations = reservations.map((reservation) => {
if (reservation.id === updatedReservation.id) {
return updatedReservation;
} else {
return reservation;
}
});
setReservations(updatedReservations);
}
function handleCancel(reservationtodelete){
const newReservations=reservations.filter(r=>r.id !== reservationtodelete)
setReservations(newReservations)
}
const renderReservations=reservations.map((reservation)=>(
<ReservationCard key={reservation.id} reservation={reservation} handleCancel={handleCancel} onUpdateReservation={handleUpdateReservation} />
))
return(
<>
{renderReservations}
</>
)
}
export default MyReservations;
Reservation Card
function ReservationCard({ reservation, handleCancel, onUpdateReservation }) {
const { name, date, time, num, contact, occasion } = reservation;
const [isEditing, setIsEditing] = useState(false);
const handleReservationUpdate = (updatedReservation) => {
setIsEditing(false);
onUpdateReservation(updatedReservation);
};
function handleDeleteClick() {
fetch(`/reservations/${reservation.id}`, {
method: "DELETE",
});
handleCancel(reservation.id);
}
return (
<>
{isEditing ? (
<Box m={4} sx={{ width: 500 }}>
<div className="overlay2">
<EditReservationForm
reservation={reservation}
onUpdateReservation={handleReservationUpdate}
/>
</div>
</Box>
) : (
<>
<Box m={4} sx={{ width: 500 }}>
<Card width={5}>
<CardContent>
<Typography variant="h5" component="div">
{reservation.restaurant.name}
</Typography>
<br />
<Typography sx={{ mb: 1.5 }} color="text.secondary">
Guest Details
</Typography>
<Typography variant="h6" component="div">
{name}
</Typography>
<Typography variant="h6">{contact}</Typography>
<Typography variant="h6">Date:{date}</Typography>
<Typography variant="h6">Time : {time}</Typography>
<Typography variant="h6">Guests : {num}</Typography>
<Typography variant="h6">Occasion:{occasion}</Typography>
</CardContent>
<CardActions>
<Button onClick={() => setIsEditing((isEditing) => !isEditing)}>
{" "}
Modify Booking
</Button>
<Button onClick={handleDeleteClick}>Cancel Booking</Button>
</CardActions>
</Card>
</Box>
</>
)}
</>
);
}
export default ReservationCard;
Fetch is an async promise function, so you'd need to use .then or await to make sure to receive the response.
const response = await fetch(
`http://localhost:8080/api/user/login?username=${uname}&password=${pass}`, {
method: 'POST'
}
);
const userData = await response.json();

FormData: The data is not placed in the formData, and an empty object is printed

I have a project in order to run a contracting, building and construction company, and I must create an invoice, and to create an invoice I must send a request to the backend, and the form of the request must be as shown in this picture:
And this is what the interface should look like:
The problem is that when I print "formData" it prints an empty object, even though I passed the data to "formData", how can I solve the problem?
this is the empty Object:
And the data is sent to the backend empty
and when i print:
console.log(formData.get("invoice"));
console.log(formData.get("data"));
the console was:
null
[object Object]
file.js:
import React, { useState } from "react";
import InputAdornment from "#material-ui/core/InputAdornment";
import TextField from "#material-ui/core/TextField";
import Grid from "#material-ui/core/Grid";
import "date-fns";
import DateFnsUtils from "#date-io/date-fns";
// import {
// KeyboardDatePicker,
// MuiPickersUtilsProvider,
// DatePicker,
// } from "#material-ui/pickers";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import CloudUploadIcon from "#material-ui/icons/CloudUpload";
import { addInvoice } from "../../../store/invoiceSlice";
import Icon from "#material-ui/core/Icon";
import { motion } from "framer-motion";
import { useDispatch } from "react-redux";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { alpha } from "#material-ui/core/styles";
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 [issueDate, setIssueDate] = useState(new Date());
const [dueDate, setDueDate] = useState(new Date());
const [netAmount, setNetAmount] = useState("");
const [taxNumber, setTaxNumber] = useState("");
const [grossAmount, setGrossAmount] = useState("");
const [file, setFile] = useState(null);
let formData = new FormData();
const fileSelectedHandler = (event) => {
console.log(event.target.files[0]);
const file = event.target.files[0];
if (event.target && file) {
formData.append("invoice", file);
setFile(file);
}
};
const uploadHandler = (event) => {
// fd.append("invoice", files);
formData.append("data", {
grossAmount,
taxNumber,
netAmount,
dueDate,
issueDate,
});
console.log("formmmmmmmmmmm Data: ", formData);
// call api
dispatch(addInvoice(formData));
};
const handleissueDateChange = (date) => {
setIssueDate(date);
console.log("date issssssssss: ", date);
console.log("date issssssssss: ", issueDate);
};
const handleDueDateChange = (date) => {
setDueDate(date);
};
const handleNetAmountChange = (event) => {
setNetAmount(event.target.value);
};
const handleTaxAmountChange = (event) => {
setTaxNumber(event.target.value);
};
const handleGrossAmountChange = (event) => {
setGrossAmount(event.target.value);
};
return (
<>
{/* <MuiPickersUtilsProvider utils={DateFnsUtils}>
<div className="flex -mx-4">
<KeyboardDatePicker
inputVariant="outlined"
className="mt-8 mb-16"
margin="normal"
id="date-picker-dialog"
label="issue Date"
format="MM/dd/yyyy"
KeyboardButtonProps={{
"aria-label": "change date",
}}
value={issueDate}
onChange={handleissueDateChange}
/> */}
<div className="flex -mx-4">
<TextField
id="date"
variant="outlined"
label="Choose Issue Date"
className="mt-8 mb-16 mr-5"
type="date"
defaultValue={issueDate}
InputLabelProps={{
shrink: true,
}}
// value={issueDate}
onChange={handleissueDateChange}
fullWidth
/>
<TextField
id="date"
variant="outlined"
className="mt-8 mb-16"
label="Choose Due Date"
type="date"
defaultValue={dueDate}
InputLabelProps={{
shrink: true,
}}
// value={dueDate}
onChange={handleDueDateChange}
fullWidth
/>
</div>
{/* <KeyboardDatePicker
inputVariant="outlined"
className="mt-8 mb-16 ml-6"
margin="normal"
id="date-picker-dialog"
label="Due Date"
format="MM/dd/yyyy"
KeyboardButtonProps={{
"aria-label": "change date",
}}
value={dueDate}
onChange={handleDueDateChange}
/>
</div>
</MuiPickersUtilsProvider> */}
<TextField
className="mt-8 mb-16"
label="Net Amount"
id="extraShippingFee"
variant="outlined"
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>,
}}
value={netAmount}
onChange={handleNetAmountChange}
fullWidth
/>
<TextField
className="mt-8 mb-16"
label="Tax Number"
id="extraShippingFee"
variant="outlined"
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>,
}}
value={taxNumber}
onChange={handleTaxAmountChange}
fullWidth
/>
<TextField
className="mt-8 mb-16"
label="Gross Amount"
id="extraShippingFee"
variant="outlined"
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>,
}}
value={grossAmount}
onChange={handleGrossAmountChange}
fullWidth
/>
<div className={classes.root}>
<input
accept="application/pdf"
// className={classes.input}
// multiple
type="file"
onChange={fileSelectedHandler}
/>
{/* <label htmlFor="contained-button-file"> */}
{/* <Button
variant="contained"
color="primary"
size="large"
className={classes.button}
startIcon={<CloudUploadIcon />}
> */}
{/* Upload */}
{/* </Button> */}
{/* </label> */}
</div>
<motion.div
className="flex"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0, transition: { delay: 0.3 } }}
>
<Button
className="whitespace-nowrap mx-4"
variant="contained"
color="secondary"
// onClick={handleRemoveProduct}
startIcon={<Icon className="hidden sm:flex">delete</Icon>}
>
Cancel
</Button>
<Button
className="whitespace-nowrap mx-4"
variant="contained"
color="secondary"
// disabled={_.isEmpty(dirtyFields) || !isValid}
onClick={uploadHandler}
>
Create
</Button>
</motion.div>
</>
);
}
export default ShippingTab;
#hiba-youssef This is not a problem, console logging formData always gives empty obj but that doesn't mean it is empty, to print the values you can use get() method , kindly refer this official documenation https://developer.mozilla.org/en-US/docs/Web/API/FormData/get.
--EDIT--
As per your postman screenshot you can modify the uploadHandler fn as below
const uploadHandler = (event) => {
const formData=new FormData();
formData.append('grossAmount',grossAmount);
formData.append('taxNumber',taxNumber);
formData.append('dueDate',dueDate);
formData.append('netAmount',netAmount);
formData.append('issueDate',issueDate);
formData.append('issueDate',issueDate);
formData.append('invoice',file);
// now the formData is as per postman screenshot. You can also add all keys to an array and loop it
};
Thanks ,
working fine when i used => Object.fromEntries(formData.entries())
const uploadHandler = (event) => {
const formData=new FormData();
formData.append('grossAmount',grossAmount);
formData.append('taxNumber',taxNumber);
formData.append('dueDate',dueDate);
formData.append('netAmount',netAmount);
formData.append('issueDate',issueDate);
formData.append('issueDate',issueDate);
formData.append('invoice',file);
console.log(Object.fromEntries(formData.entries()))
};
if you send data in api using formData you must remember.
1- if data with text and image you need to use inside header
headers = {
"Content-Type": "multipart/form-data",
};
2- if data only text use
headers = {
"Content-Type": "application/json",
};

Store country names in ISO 3166 format in mongoDB

In my flight booking application the admin enters destination names while adding new flights. The admin can write the full country/City names (i.e. Sydney, New York etc.) But I want to save them as 3 letter abbreviation such as (SYD, NYC etc.) in the database when admin clicks the submit button. I tried using the i18n-iso-countries package but I'm not quite sure of implementing this. Is there any simple approach to this?
This is my form component:
import React, { useState } from "react";
import {
TextField,
FormControl,
InputLabel,
MenuItem,
Select,
RadioGroup,
FormControlLabel,
Radio,
Button,
} from "#mui/material";
import Autocomplete from "#mui/material/Autocomplete";
import DatePicker from "#mui/lab/DatePicker";
import AdapterDateFns from "#mui/lab/AdapterDateFns";
import LocalizationProvider from "#mui/lab/LocalizationProvider";
import SendIcon from "#mui/icons-material/Send";
import "./Form.css";
const destinations = [
{ title: "Sydney" },
{ title: "Paris" },
{ title: "Thailand" },
{ title: "France" },
{ title: "Dhaka" },
{ title: "Bangkok" },
{ title: "Indonesia" },
];
export default function Form() {
const dt = new Date().toLocaleDateString();
console.log(dt);
const [formData, setFormData] = useState({
from: "",
to: "",
depart: dt,
return: dt,
noOfPassenger: 1,
tripType: "Round Trip",
});
const defaultProps = {
options: destinations,
getOptionLabel: (option) => option.title,
};
const handleSubmit = () => {
if (formData.tripType === "One Way")
setFormData({ ...formData, return: "None" });
console.log(formData);
};
return (
<div className="form_parent">
<div className="inner_form">
<div className="from">
<Autocomplete
{...defaultProps}
onChange={(event, value) =>
setFormData({ ...formData, from: value.title })
}
renderInput={(params) => (
<TextField {...params} label="From" variant="outlined" required />
)}
/>
</div>
<div className="to">
<Autocomplete
{...defaultProps}
onChange={(event, value) =>
setFormData({ ...formData, to: value.title })
}
renderInput={(params) => (
<TextField {...params} label="To" variant="outlined" required />
)}
/>
</div>
<div className="dates">
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Departure Date"
value={formData.depart}
disablePast
onChange={(e) =>
setFormData({
...formData,
depart: e.toLocaleDateString(),
})
}
renderInput={(params) => <TextField {...params} required />}
/>
</LocalizationProvider>
</div>
<div className="dates">
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Return Date"
value={formData.return}
disablePast
disabled={formData.tripType === "One Way" ? true : false}
onChange={(e) =>
setFormData({
...formData,
return: e.toLocaleDateString(),
})
}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
</div>
<div className="dates">
<FormControl className="passenger">
<InputLabel id="passengerNo">No. of Passenger</InputLabel>
<Select
variant="outlined"
labelId="passengerNo"
value={formData.noOfPassenger}
required
label="No. of Passenger"
onChange={(e) =>
setFormData({ ...formData, noOfPassenger: e.target.value })
}
>
<MenuItem value={1}>1</MenuItem>
<MenuItem value={2}>2</MenuItem>
<MenuItem value={3}>3</MenuItem>
<MenuItem value={4}>4</MenuItem>
</Select>
</FormControl>
</div>
<div className="radio_buttons">
<FormControl component="fieldset">
<RadioGroup
row
aria-label="trip"
name="row-radio-buttons-group"
value={formData.tripType}
onChange={(e) =>
setFormData({ ...formData, tripType: e.target.value })
}
>
<FormControlLabel
value="One Way"
control={<Radio />}
label="One Way"
/>
<FormControlLabel
value="Round Trip"
control={<Radio />}
label="Round Trip"
/>
</RadioGroup>
</FormControl>
</div>
<div className="submit">
<Button
className="submit_btn"
variant="contained"
color="error"
onClick={handleSubmit}
endIcon={<SendIcon />}
>
Submit
</Button>
</div>
</div>
</div>
);
}
Model
import mongoose from "mongoose";
const flightSchema = mongoose.Schema({
from: String,
to: String,
price: String,
flightType: String,
airline: String,
date: {
type: Date,
default: new Date().toLocaleDateString(),
},
});
const FlightData = mongoose.model("FlightData", flightSchema);
export default FlightData;

Using a checkbox with Material UI to change the boolean value of an attribute within redux

I have a form that is controlled by redux state and I need to use checkboxes that will change the value of certain attributes of state from true to false and back again. The way that I have it written, I can click the checkbox and it updates the attribute to true but when I try to click on it again, it doesn't uncheck and it doesn't change state.
Here is my form,
import React, { useEffect } from 'react';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import CssBaseline from '#material-ui/core/CssBaseline';
import TextField from '#material-ui/core/TextField';
import {Link} from 'react-router-dom'
import Grid from '#material-ui/core/Grid';
import Box from '#material-ui/core/Box';
import EditIcon from '#material-ui/icons/Edit';
import Typography from '#material-ui/core/Typography';
import { makeStyles } from '#material-ui/core/styles';
import Container from '#material-ui/core/Container';
import {connect} from 'react-redux'
import {updateProfileForm, setProfileForm} from '../actions/updateProfileActions'
import { update } from '../actions/currentUserActions'
import FormControl from '#material-ui/core/FormControl';
import Checkbox from '#material-ui/core/Checkbox';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormGroup from '#material-ui/core/FormGroup';
import FormLabel from '#material-ui/core/FormLabel';
import { TimePicker } from "#material-ui/pickers"
function Copyright() {
return (
<Typography variant="body2" color="textSecondary" align="center">
{'Copyright © '}
<Link color="inherit" href="https://material-ui.com/">
NTXASN
</Link>{' '}
{new Date().getFullYear()}
{'.'}
</Typography>
);
}
const useStyles = makeStyles((theme) => ({
paper: {
marginTop: theme.spacing(8),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(3),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
}));
const UpdateProfileForm =({updateFormData, updateProfileForm, update, history, currentUser, setProfileForm })=> {
const useUpdateProfileForm = () => {
useEffect(()=> {
setProfileForm(currentUser.attributes)
}, [])
}
useUpdateProfileForm()
const classes = useStyles();
const handleChange = event => {
const {name, value } = event.target
const updatedFormInfo = {
...updateFormData,
[name]: value
}
updateProfileForm(updatedFormInfo)
}
const handleBoolean = event => {
const {name, value } = event.target
console.log(event.target.checked)
const updatedFormInfo = {
...updateFormData,
[name]: !value
}
updateProfileForm(updatedFormInfo)
console.log(event.target.checked)
}
const handleSubmit = event =>{
event.preventDefault()
update(updateFormData, history)
}
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<EditIcon />
</Avatar>
<Typography component="h1" variant="h5">
Update your Driver Profile
</Typography>
<form className={classes.form} noValidate onSubmit={handleSubmit}>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
name="name"
variant="outlined"
required
fullWidth
id="name"
label="Name"
autoFocus
onChange={handleChange}
value={updateFormData.name}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
onChange={handleChange}
value={updateFormData.email}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="phone_number"
label="Phone Number"
name="phone_number"
autoComplete="phone number"
onChange={handleChange}
type="tel"
value={updateFormData.phone_number}
/>
</Grid>
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">Select Available Days</FormLabel>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={updateFormData.monday} onChange={handleBoolean} name="monday" />}
label="Monday"
/>
<FormControlLabel
control={<Checkbox checked={updateFormData.tuesday} onChange={handleBoolean} name="tuesday" />}
label="Tuesday"
/>
<FormControlLabel
control={<Checkbox checked={updateFormData.wednesday} onChange={handleBoolean} name="wednesday" />}
label="Wednesday"
/>
</FormGroup>
</FormControl>
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">-</FormLabel>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={updateFormData.thursday} onChange={handleBoolean} name="thursday" />}
label="Thursday"
/>
<FormControlLabel
control={<Checkbox checked={updateFormData.friday} onChange={handleBoolean} name="friday" />}
label="Friday"
/>
<FormControlLabel
control={<Checkbox checked={updateFormData.saturday} onChange={handleBoolean} name="saturday" />}
label="Saturday"
/>
</FormGroup>
</FormControl>
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">-</FormLabel>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={updateFormData.sunday} onChange={handleBoolean} name="sunday" />}
label="Sunday"
/>
</FormGroup>
</FormControl>
</Grid>
<br/>
<Grid>
<TimePicker autoOk label="Hour Availability Lower Limit" value={updateFormData.availability_hours_lower} name="availability_hours_lower" onChange={handleChange}/>
<TimePicker autoOk label="Hour Availability Upper Limit" value={updateFormData.availability_hours_lower} name="availability_hours_upper" onChange={handleChange}/>
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Update Profile
</Button>
</form>
</div>
<Box mt={5}>
<Copyright />
</Box>
</Container>
);
}
const mapStateToProps = state => {
return{
currentUser: state.currentUser,
updateFormData: state.updateProfile
}
}
export default connect(mapStateToProps, {updateProfileForm, update, setProfileForm})(UpdateProfileForm)
here are my actions
export const updateProfileForm = (formData) => {
return {
type: "UPDATE_PROFILE_FORM",
formData: formData
}
}
export const resetProfileForm = () => {
return {
type: "RESET_PROFILE_FORM",
}
}
export const setProfileForm = (formData) => {
return {
type: 'SET_PROFILE_FORM',
formData: formData
}
}
here is my reducer
const initialState = {
name: '',
email: '',
phone_number: '',
monday: false,
tuesday: false,
wednesday: false,
thursday: false,
friday: false,
saturday: false,
sunday: false,
availability_hours_lower: '',
availability_hours_upper: ''
}
const updateProfileReducer = (state = initialState, action) => {
switch(action.type){
case "UPDATE_PROFILE_FORM":
console.log(action.formData)
return action.formData
case "RESET_SIGNUP_FORM":
return initialState
case "SET_PROFILE_FORM":
return action.formData
default:
return state
}
}
export default updateProfileReducer
As I stated, right now when I click the check box it updates redux state from false to true and renders a checked box but when I try to uncheck the box, redux state is not updated and it does not uncheck the box. Any help would be greatly appreciated!
You should use the checked attribute to check if the checkbox is on or not.
const handleBoolean = (event) => {
const { name, checked } = event.target
updateProfileForm({
...updateFormData,
[name]: checked,
})
}
This is unrelated to the question but you can refactor your handleChange and handleBoolean functions by updating the reducer to apply the changes to the existing state.
const updateProfileReducer = (state = initialState, action) => {
switch (action.type) {
case 'UPDATE_PROFILE_FORM':
return { ...state, ...action.formData }
// ...
default:
return state
}
}
const handleChange = (event) => {
const { name, value } = event.target
updateProfileForm({
[name]: value,
})
}
const handleBoolean = (event) => {
const { name, checked } = event.target
updateProfileForm({
[name]: checked,
})
}
Btw, you don't even need to manage the form state in Redux since it's a local state. I would recommend using React Hook Form to simplify the code.

React Final Form Material UI creating double lines on input field

I have a strange thing happening where I can't really figure out what is creating those double input field lines in my code (see the picture). I have literally copied the code from the official website (React Final Form) and it doesn't seem to be happening there. Does it have something to do with the browser compatibility?
My code:
import React from 'react';
import { Form } from 'react-final-form';
import {
TextField,
Select,
} from 'mui-rff';
import {
Grid,
Button,
MenuItem,
} from '#material-ui/core';
import { createMuiTheme } from '#material-ui/core/styles';
import { ThemeProvider } from '#material-ui/styles';
const theme = createMuiTheme({
palette: {
primary: {
main: '#008080',
},
}
})
const onSubmit = async values => {
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const validate = values => {
const errors = {};
if (!values.firstName) {
errors.firstName = 'Required';
}
if (!values.lastName) {
errors.lastName = 'Required';
}
if (!values.email) {
errors.email = 'Required';
}
return errors;
};
const formFields = [
{
size: 6,
field: (
<TextField
label="First Name"
name="firstName"
margin="none"
required={true}
/>
),
},
{
size: 6,
field: (
<TextField
label="Last Name"
name="lastName"
margin="none"
required={true}
/>
),
},
{
size: 12,
field: (
<TextField
type="email"
label="Email"
name="email"
margin="none"
required={true}
/>
),
},
{
size: 12,
field: <TextField name="notes" multiline label="Notes" margin="none" />,
},
{
size: 12,
field: (
<Select
name="city"
label="Select a City"
formControlProps={{ margin: 'none' }}
>
<MenuItem value="London">London</MenuItem>
<MenuItem value="New York">New York</MenuItem>
<MenuItem value="Tallinn">Tallinn</MenuItem>
</Select>
)
}
];
const FinalForm = () => {
return (
<div className="col s12 m6">
<div className="card-panel grey lighten-3">
<h5>Please fill out this form</h5>
<Form
onSubmit={onSubmit}
validate={validate}
render={({ handleSubmit, form, submitting, pristine }) => (
<form onSubmit={handleSubmit} noValidate>
<Grid container alignItems="flex-start" spacing={2}>
{formFields.map((item, idx) => (
<Grid item xs={item.size} key={idx}>
{item.field}
</Grid>
))}
<Grid item style={{ marginTop: 16 }}>
<Button
type="button"
variant="contained"
onClick={form.reset}
disabled={submitting || pristine}
>
Reset
</Button>
</Grid>
<Grid item style={{ marginTop: 16 }}>
<ThemeProvider theme={theme}>
<Button
variant="contained"
type="submit"
color='primary'
disabled={submitting}
>
Submit
</Button>
</ThemeProvider>
</Grid>
</Grid>
</form>
)}
/>
</div>
</div>
)
};
export default FinalForm;

Resources