React Slide Animation removing selected value on unmountOnExit - reactjs

I am facing a issue, on click of radio button the next question comes using slide animation, But on unmounting on Exit the previously selected radio button value is also getting deselected. How to prevent this from happening.
const Quiz = (props) => {
const {questions, quiz, options} = useSelector((state) => state.quiz);
const [user, setuser] = useState("");
const [currentQuestion, setCurrentQuestion] = useState(0);
const [checked, setChecked] = useState(true);
const [option, setOptions] = useState("");
const dispatch = useDispatch();
const history = useHistory();
const handleRadioChange = (number, event) => {
let currentSelection = questions.find(question => question.number === number);
console.log(currentSelection + "radio selected");
currentSelection.value = event.target.value;
questions[currentQuestion].value = event.target.value;
console.log(questions[currentQuestion].value + "value added");
setCurrentQuestion((current) => {
return Math.min(
current + 1,
questions.length - 1
);
});
setChecked((previousState) => !previousState);
setTimeout(() => {
setChecked(previousState => ({
afterPreviousChange: previousState.previousChange
}))
}, 1000);
};
const previousQuestion = (event) => {
event.preventDefault();
let new_current_questions = Math.max(currentQuestion - 1, 0);
setCurrentQuestion(new_current_questions);
setChecked((previousState) => !!previousState);
const a = setTimeout(() => {
setChecked(previousState => ({
afterPreviousChange: previousState.previousChange
}))
}, 1000);
};
function handleSubmit(event) {
event.preventDefault();
const valid = questions.some((q) => !q.value);
console.log(valid + "questionsalpha");
if (!valid) {
dispatch(postQuiz({ responses: questions, id: quiz.id }, history));
}
setChecked((previousState) => !previousState);
const a = setTimeout(() => {
setChecked((previousState) => !previousState);
setCurrentQuestion(0);
},1000);};
return(
<Slide direction="up"
in={checked}
appear={true}
mountOnEnter
unmountOnExit
timeout={{ enter: 1000 , exit: checked ? 1 : 900}}
>
<Card variant="outlined" sx={{ bgcolor: "#bea"}} elevation={0}>
<CardContent>
<form onSubmit= {handleSubmit}>
<CardActions>
<Button type="submit" color="warning" variant="outlined" disabled={currentQuestion===0} className={classes.button} onClick={previousQuestion}>
Previous</Button>
</CardActions>
<FormControl component='fieldset' className={classes.formControl}
data-hidden={questions[currentQuestion].number !==
currentQuestion[questions[currentQuestion].number]} >
<FormLabel component='legend'>
{questions[currentQuestion].question}
</FormLabel>
<FormLabel component='legend'>
{questions[currentQuestion].description}
</FormLabel>
<RadioGroup
aria-label='quiz'
name='quiz'
defaultValue={' '}
value={questions[currentQuestion].value}
checked={checked}
onChange={(e)=> handleRadioChange(questions[currentQuestion].number, e)}
sx={{
color: pink[800],
'&.Mui-checked': {
color: blue[600],
},
}}>
{options.map((option) => (
<FormControlLabel
key={option.score}
value={option.score}
control={<Radio sx={{
color: pink[800],
'&.Mui-checked': {
color: blue[600],
},
}}/>}
label={option.label}
/>
))}
</RadioGroup>
</FormControl>
<CardActions>
<Button type="submit" variant="contained" color="primary" className={classes.button} disabled={currentQuestion<5} onClick={handleSubmit}>
Submit
</Button>
</CardActions>
</form>
</CardContent>
</Card>
</Slide>
);
function using handleChange and previous button is also I have shared. Please help with this issue.

The main reason for previously radio button value not persist is because you are not changing the questions state with the update values, you are just changing the object itself.
The error is specifically here:
let currentSelection = questions.find(question => question.number === number);
console.log(currentSelection + "radio selected");
// HERE YOU ARE CHANGIG ONLY THE OBJECT, NOT THE ARRAY
currentSelection.value = event.target.value;
questions[currentQuestion].value = event.target.value;
One way to do this is map the questions array, change the current question value and then update the questions state:
const updatedQuestions = questions.map((item) => {
if (item.number === currentQuestion) item.value = event.target.value;
return item;
});
setQuestions(updatedQuestions);
But your code also have other inconsistencies, i did a code sample for you with comments.

Related

React Hooks form OnSubmit makes Problems

Hey i cant find a solution why my code dosent work. When i trigger the button nothing happens, normally i want to display the data but nothing happend. My Code is in attachment.
if someone has an idde to solve the problem I would be very grateful.
I have looked at other posts but nothing helps with my Problem.
export default function FormDialog(props) {
const [open, setOpen] = React.useState(false);
const {
control,
formState: { errors },
setValue,
} = useFormContext();
let isError = false;
let errorMessage = "";
if (errors && errors.hasOwnProperty(name)) {
isError = true;
errorMessage = errors[name].message;
}
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
props.func();
};
const handleChange = (event) => {
props.setMenge(event.target.value);
};
React.useEffect(() => {
if (errors) {
console.log(errors);
}
}, [errors]);
return (
<>
<Button variant="contained" onClick={handleClickOpen}>
Umbuchen
</Button>
<Dialog open={open} onClose={handleClose}>
<DialogTitle>Umbuchung</DialogTitle>
<DialogContent>
<DialogContentText>
Bitte geben Sie die Menge ein die umgelagert werden soll!
</DialogContentText>
<Controller
control={control}
name={props.name}
render={({ field }) => (
<TextField
{...field}
label={props.label}
error={isError}
helperText={errorMessage}
autoFocus
margin="dense"
id="menge"
type="number"
fullWidth
variant="standard"
/>
)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Schließen</Button>
<Button variant="contained" color="primary" type="submit">
Umbuchen
</Button>
</DialogActions>
</Dialog>
</>
);
}
export default function Home() {
const [sEingang, setSEingang] = useState([]);
const [sLager, setSLager] = useState("");
const [menge, setMenge] = useState(0);
const context = React.useContext(AuthContext);
const { data: warendata } = useWaren(context.access_token);
const { data: lagerdata } = useGetLager(context.access_token);
const methods = useForm({
resolver: yupResolver(validationShema),
mode: "all",
});
const { getValues, reset, watch, handleSubmit } = methods;
const waren = watch("selWaren");
const { data: chargendata, refetch } = useChargen({
access_token: context.access_token,
lager: waren,
});
const { mutateAsync, isError: mutError, isLoading, isSuccess } = useAnlegen();
React.useEffect(() => {
async function getToken() {
const res = await context.setTokenSilently();
}
const interval = setInterval(() => {
getToken();
}, 30000);
return () => clearInterval(interval);
}, []);
const onSubmit = (data) => {
console.log("data: ", data);
};
return (
<>
<CssBaseline />
<ButtonAppBar name={"Wareneingänge"} back={false} onClick={reset} />
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}>
<Grid
container
spacing={0}
direction="column"
alignItems="center"
justifyContent="center"
sx={{p: 2, m: 3}}
style={{ minHeight: "50vh", }}
>
<Grid item xs={3}>
<MySelect
formname={"selWaren"}
data={warendata}
name={"Wareneingänge"}
selected={sEingang}
setSelected={setSEingang}
reset={reset} />
</Grid>
<Grid item xs={3}>
<MySelect
formname={"selLager"}
data={lagerdata}
name={"Freie Lagerplätze"}
selected={sLager}
setSelected={setSLager} />
</Grid>
<Grid item xs={3}>
<MySelect
formname={"selChargen"}
data={chargendata}
name={"Freie Chargenplätze"}
selected={sLager}
setSelected={setSLager} />
</Grid>
<Grid item xs={3} sx={{ m: 3}}>
<FormDialog
func={onSubmit}
name={"selMenge"} />
</Grid>
</Grid>
</form>
</FormProvider>
</>
);
}
It looks like you've forgotten to add an onClick handler for the submit button here:
<Button variant="contained" color="primary" type="submit">
Umbuchen
</Button>
Something like this should work:
<Button onClick={() => props.func()} variant="contained" color="primary" type="submit">
Umbuchen
</Button>

After editing data, redux cannot read id

i have a problem.
I use a form to edit my data, then when i want to see edited data, i get an ×
TypeError: Cannot read properties of undefined (reading 'id')
Pointing at my
{users &&
users.map((user) => {
return (
<div key={user.id}>
<Link to={`users/${user.id}`}> {user.name} </Link>
</div>
);
})}
Which is used to display data.
After refreshing the site (F5) it works, so i assume that the redux has problem with reading edited data for the first time, altough it do work with adding new data. anyone know what i can do?
My UserEditForm:
const UserEditForm = () => {
let { id } = useParams();
const { user } = useSelector((state) => state.user);
const [state, setState] = useState({
name: "",
birthday: "",
img: "",
});
const [error, setError] = useState("");
console.log(id);
let history = useHistory();
let dispatch = useDispatch();
const { name, birthday, img } = state;
useEffect(() => {
dispatch(getSingleUser(id));
}, []);
useEffect(() => {
if (user) {
setState({ ...user });
}
}, [user]);
const handleInputChange = (e) => {
let { name, value } = e.target;
setState({ ...state, [name]: value });
};
const handleSubmit = (e) => {
dispatch(updateUser(state, id));
history.push("/");
setError("");
};
return (
<div>
<Button
style={{ width: "100px", marginTop: "20px" }}
variant="contained"
color="secondary"
onClick={() => history.push("/")}
>
Go Back
</Button>
<h2>Edit User</h2>
{error && <h3 style={{ color: "red" }}>{error}</h3>}
<form noValidate autoComplete="off" onSubmit={handleSubmit}>
<TextField
id="standard-basic"
label="Name"
value={name || ""}
name="name"
type="text"
onChange={handleInputChange}
/>
<br />
<TextField
id="standard-basic"
label="birthday"
name="birthday"
value={birthday || ""}
type="birthday"
onChange={handleInputChange}
/>
<br />
<TextField
id="standard-basic"
label="img"
value={img || ""}
name="img"
type="number"
onChange={handleInputChange}
/>
<Button
style={{ width: "100px" }}
variant="contained"
color="primary"
type="submit"
onChange={handleInputChange}
>
Update
</Button>
</form>
</div>
);
};
export default UserEditForm;
My UserList component:
const UserList = ({ users, history }) => {
const dispatch = useDispatch();
const fetchUsers = async () => {
const response = await axios
.get("http://localhost:3000/characters")
.catch((err) => {
console.log("Err: ", err);
});
dispatch(setUsers(response.data));
};
useEffect(() => {
fetchUsers();
}, []);
console.log(users);
return (
<div>
<button onClick={() => history.goBack()}>...back</button>
<li>
<Link to="/user/add">Add Users</Link>
</li>
{users &&
users.map((user) => {
return (
<div key={user.id}>
<Link to={`users/${user.id}`}> {user.name} </Link>
</div>
);
})}
</div>
);
};
const mapStateToProps = (state) => {
return {
users: state.allUsers.users,
};
};
export default connect(mapStateToProps, null)(UserList);

How to get multiple checkbox values on submit reactjs

I have a multiple recepient email and multiple checkbox column
I want to get each recepient email and checkbox values on submit.I am getting recepient emails on submit but no checkbox values. Kindly help
The form looks like this.
Here is my code
export default function ShareReportView(props) {
const [recipientEmails, updateRecicpientEmails] = useState({});
const handleInputChange = (e, name) => {
updateRecicpientEmails((prevState) => ({
...prevState,
[name]: e.target.value,
}));
};
const extratEmailList = (emailsList) => {
if (!emailsList || !Object.keys(emailsList).length) {
return;
}
console.log('obj email list',Object.values(emailsList))
return Object.values(emailsList);
};
const handlepermission = () => {
};
function sendEmail(recipientEmailsList) {
const rEmails = extratEmailList(recipientEmailsList);
console.log(rEmails);#prints all emails here
#here i want to get all checkbox values here on submit
}
return (
<div className="container">
{[...Array(count)].map((val, index) => (
<div key={index} className={`${styles["textField"]}`}>
<div style={{ float: "left" }}>
<Box
component="form"
sx={{
"& > :not(style)": { marginRight: 4, width: "31ch" },
}}
noValidate
autoComplete="off"
>
{" "}
<FormControl variant="standard">
<InputLabel
htmlFor="component-simple">
Recipient E mail
</InputLabel>
<Input
id="component-simple"
onChange={(event) =>
handleInputChange(
event,
`recipient_email_${index++}`,
false
)
}
name={`recipient_email_${index++}`}
key={`recipient_email_${index++}`}
disableUnderline={true}
/>
</FormControl>
<FormControlLabel
control={
<Checkbox
color="default"
onClick={() => {
handlepermission(`${index++}`);
}}
/>
}
label="Allow user to perfrom action"
name={`allow_user_edit_${index++}`}
/>
</Box>
</div>
</div>
))}
<div className="btn">
<button
className={`${styles.send}`}
onClick={() => sendEmail(recipientEmails)}
>
SEND
</button>
</div>
</div>
)}
I am not on my computer but following should work
export default function ShareReportView(props) {
const [recipientEmails, updateRecicpientEmails] = useState([]);
const handleEmailChange = (e, index) => {
let temp = [...recipientEmails]
let tempObj = {...temp[index]}
tempObj.email = e.target.value
temp.splice(index, 1, tempObj)
updateRecicpientEmails(temp)
};
const handlePermissionChange = (e, index) => {
let temp = [...recipientEmails]
let tempObj = {...temp[index]}
tempObj.permission = e.target.value
temp.splice(index, 1, tempObj)
updateRecicpientEmails(temp)
};
function sendEmail(recipientEmailsList) {
recipientEmails.forEach(e => {
console.log(e.email, e.permission)
})
}
return (
<div className="container">
{[...Array(count)].map((val, index) => (
<div key={index} className={`${styles["textField"]}`}>
<div style={{ float: "left" }}>
<Box
component="form"
sx={{
"& > :not(style)": { marginRight: 4, width: "31ch" },
}}
noValidate
autoComplete="off"
>
{" "}
<FormControl variant="standard">
<InputLabel
htmlFor="component-simple">
Recipient E mail
</InputLabel>
<Input
id="component-simple"
onChange={(event) =>
handleEmailChange(
event,
index
)
}
name={`recipient_email_${index++}`}
key={`recipient_email_${index++}`}
disableUnderline={true}
/>
</FormControl>
<FormControlLabel
control={
<Checkbox
color="default"
onClick={(e) => {
handlePermissionChange(e, index);
}}
/>
}
label="Allow user to perfrom action"
name={`allow_user_edit_${index++}`}
/>
</Box>
</div>
</div>
))}
<div className="btn">
<button
className={`${styles.send}`}
onClick={() => sendEmail(recipientEmails)}
>
SEND
</button>
</div>
</div>
)}
Let me know if you feel any issues, will be happy to help you, you should also change the logic of add and remove entries button. On add button just add a new object with empty values in recipientEmails list. and use your map function in render on recipientEmails.
Edit # 1
function addNewEntry(){ //call this on add new entry button
let temp = [...recipientEmails]
temp.push({
email: '',
permission: false
})
updateRecicpientEmails(temp)
}
you can use addNewEntry for adding new row. but now your will have to edit your render function something like this
replace {[...Array(count)].map((val, index) => (
with {recipientEmails.map((val, index) => (
in your return staement
You need to save multiple values on the same object per recipient, I did this change on your handleInputChange function, now it creates an object per recipient
const handleInputChange = (e, name) => {
updateRecicpientEmails((prevState) => ({
...prevState,
[name]: {
email: e.target.value
}
}));
};
and I call it like this
handleInputChange(event, `recipient_${index}`, false)
removed _email from there.
And for the handle permission, just add a new property to the recipient object with the checkbox value
const handlepermission = (index, value) => {
updateRecicpientEmails((currentRecipients) => ({
...currentRecipients,
[index]: {
...currentRecipients[index],
allow: value
}
}));
};
this function runs on input change, so just add this to prop to the input:
onChange={({ target: { checked } }) => {
handlepermission(`recipient_${index}`, checked);
}}
To be honest is easier if you use the native form submit handler and FormData API, here is an example:
https://codesandbox.io/s/formdata-api-example-xkvi8

Submitting a form React - MUI v5, delivers a wrong value from one field

I have a react from made with MUI (I'm using MUI v5))
Everything works ok, until I hit submit.
The submitted values are ok except for the select, which is delayed, I mean, suppose I select the first value, and the default value is 0, then hitting submit will say that the selected value is 0, then, if I hit submit once more, immediately, then it says the correct answer, see, it’s kind of delayed by one render of the component.
I wanted to have all values in one object state, but I had to separate some of them, like a date value (uploadDate) and the select value (category value)
This is the simplified code:
const AddVideoAndCategories = ({
addVideo,
getAllCategoriesWithoutVideos,
categories,
categoriesLoaded,
resetDrawerMenuItems,
addDrawerItem,
setCurrentDrawerMenuItems
}) => {
const [ formValues, setFormValues ] = useState(defaultValues);
const [ uploadDate, setUploadDate ] = React.useState(today());
const [ selectedCategory, setSelectedCategory ] = useState('');
const { handleSubmit, reset, control } = useForm();
const navigate = useNavigate();
useEffect(() => {
if (!categoriesLoaded) {
/// Load the categories from the back end to redux
}
}, []);
const handleCategoryChange = (e) => {
setSelectedCategory(e.target.value);
};
const handleInputChange = (e) => {
let { name, value } = e.target;
setFormValues({
...formValues,
[name]: value
});
};
async function handleCtrlClick(target) {
/// allow pasting the clipboard with just ctrl click
}
const handleClick = (event) => {
// In that case, event.ctrlKey does the trick.
if (event.ctrlKey) {
event.stopPropagation();
handleCtrlClick(event.target);
}
};
const myHandleSubmit = () => {
let arr = selectedCategory.split('^'); /// This trick allowed me to show the selected category name instead of the categoryId
setFormValues({
...formValues,
uploadDate: uploadDate,
categoryId: arr[0]
});
alert(JSON.stringify(formValues, null, 4));
};
const handleReset = () => {
/// resets all fields
};
return (
<form onSubmit={handleSubmit}>
<Box
component="form"
sx={{
'& > :not(style)': { m: 1, width: '30ch', marginTop: '50px', marginBottom: '0' }
}}
noValidate
autoComplete="off"
>
<Stack spacing={3}>
<TextField
…
/>
<TextField
…
/>
<TextField
…
/>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
id="author-uploadDate"
name="uploadDate"
label="Video UploadDate"
value={uploadDate}
defaultValues={uploadDate}
onChange={(newValue) => {
setUploadDate(newValue);
}}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
<TextField
…
/>
<FormControl sx={{ minWidth: 120, maxWidth: '500px', width: '270px' }}>
<InputLabel id="video categories">Video Category</InputLabel>
<Select
labelId="video categories"
id="simple-select"
value={selectedCategory}
label="Video Category"
onChange={handleCategoryChange}
>
{categories.map((category) => {
return (
<MenuItem value={category.id.toString() + '^' + category.name}>
{category.name}
</MenuItem>
);
})}
</Select>
</FormControl>
<TextField
…
/>
</Stack>
<Button variant="contained" endIcon={<SendIcon />} size="medium" onClick={myHandleSubmit}>
Submit
</Button>
<Button variant="outlined" startIcon={<DeleteIcon />} size="medium" onClick={handleReset}>
Reset
</Button>
</Box>
</form>
);
};
// title, origin, author, uploadDate, url, excerpt
const defaultValues = {
title: '',
origin: 'Vimeo',
author: '',
uploadDate: today(),
categoryId: 0,
url: '',
excerpt: ''
};
function mapStateToProps(state) {
…
}
function mapDispatchToProps(dispatch) {
…
}
export default connect(mapStateToProps, mapDispatchToProps)(AddVideoAndCategories);
Can somebody see why that happens?

useState not updating data when passing from parent function component using ref

I am trying to send data to child function component where I am binding form fields with that data. It works fine on first call, but when I am calling 2nd time the data never update in state, its always shows the first one.
This is parent which use the ref of child component
export default function Form1() {
const [count, setCount] = useState(0);
const [counter, setCounter] = useState(10);
const AddNewRef = useRef();
const clickMe=() => {
setCount(count+1);
setCounter(counter*2);
AddNewRef.current.showDrawer(counter*2);
}
return (
<div>
<p>You clicked count: {count} & counter: {counter} times</p>
{
count > 10 ?
(
<p className='red'>your count is greater then 10</p>
) :
(
<p className='green'>your count is less then 10</p>
)
}
<button onClick={() => clickMe()}>
Click me
</button>
<AddNew ref={AddNewRef} Count={count} Counter={counter} />
</div>
)
}
This is child component
const AddNew=forwardRef((props, ref) => {
const[objCounter, setobjCounter] = useState(null);
useImperativeHandle(
ref,
() => ({
showDrawer(count) {
setobjCounter(count);
//only shows at first click at parent, Not updating on 2nd, 3rd click from parent and so on....
}
}),
)
return (
<>
<Drawer
title={<span> <i className='fa-solid fa-kaaba' /> Haj Setup Form</span>}
width={window.innerWidth > 900 ? 800 : window.innerWidth - 50}
onClose={onClose}
visible={visible}
bodyStyle={{ paddingBottom: 80 }}
extra={
<Space>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={onClose} type="primary">
Submit
</Button>
</Space>
}
>
<Form
style={{display: formVisible ? 'block' : 'none'}}
form={form}
layout="vertical"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
hideRequiredMark>
<Row gutter={16}>
<Col xs={24} sm={24} md={24} lg={24}>
<Form.Item
name="packageName"
label="Package Name"
rules={[{ required: true, message: 'Please enter package name' }]}
initialValue={objCounter}
>
<Input style={{width: '100%'}}
maxLength={100} />
</Form.Item>
</Col>
</Row>
</Form>
</Drawer>
</>
)
});
export default AddNew
Since the state updates are working and you are simply wanting to update the form field, you can use the returned form reference from the useForm hook to update the form state. In this case, update the packageName field.
const AddNew = forwardRef((props, ref) => {
const [objCounter, setobjCounter] = useState(13);
const [visible, setVisible] = useState(false);
const [formVisible, setformVisible] = useState(true);
const [form] = Form.useForm();
useImperativeHandle(ref, () => ({
showDrawer(count) {
setobjCounter(count);
setVisible(true);
form.setFieldsValue({
packageName: count // <-- update the specific field
});
}
}));
const onClose = () => {
setVisible(false);
};
return (
...
);
});

Resources