I’m a newbie in React and just started to develop an admin panel on local to practice what I have learned and I have come across this problem and after searching for a long while and being unable to solve it I’m this close to step on kittens and innocent children …
so here’s the situation:
I have a panel for registering some products, say, Shoes and on my Dashboard page of admin panel I have my products listed and I can click on edit on any of them to redirect to edit page and change the info like name, price, image etc.
Up to this point everything works as intended, the problem is I have a Switch Input which indicates if it’s a featured product or not and when I land on the edit page it displays correctly if it’s On or Off, now when I click on it to change its state, the first time I click nothing happens and it starts working after 2nd click onwards.
I’ve done my research and came to know it has to do with using useEffect() and such and I’ve changed my code accordingly and now the real problem is when the switch is off when I land on edit page, on first click it works just fine and it changes to On and I can save it and it’ll do as intended but if it’s On by default when I land on the edit page, the first click won’t work and it takes 2 click and more to change the state and it escapes me how it works fine when it’s off and not both ways.
p.s it’s my first time asking question here so I might have failed to express my problem clearly or properly enough so my apologies in advance.
Here’s the code:
import {useEffect, useState} from "react";
import {useNavigate, useParams} from "react-router-dom";
import getOneProduct from "../../../Services/getOneProduct";
import {useProductsDispatch} from "../../../Context/productsContext";
import {toast} from "react-toastify";
function EditProductForm() {
const dispatch = useProductsDispatch()
const redirect = useNavigate()
const [product, setProduct] = useState({
name: '',
desc: '',
price: '',
offPrice: '',
image: '',
feat: null,
uid: '',
date: '',
})
const [switchVal, setSwitchVal] = useState(); // this is the state I use for the switch but would rather have it in the 'product' state above so they are all together
const switchHandler = (e) => { // the onChange function for the switch
const curVal = e.target.checked
setSwitchVal(curVal)
}
useEffect(() => { // the useEffect to reflect switch changes
setProduct({
...product,
feat: switchVal
})
console.log(switchVal)
}, [switchVal, setSwitchVal]);
const changeHandler = (e) => { // the input handler for all other inputs
setProduct({
...product,
[e.target.name]: e.target.value,
})
}
const editProductSubmit = (e) => { // form onSubmit
e.preventDefault()
dispatch({
type: 'EDIT_PRODUCT',
payload: {
productId,
product
}
})
redirect("/admin/dashboard")
}
const fetchedId = useParams()
const productId = fetchedId.id
useEffect(() => {
const fetchedProduct = async () => {
try {
const {data} = await getOneProduct(productId) // axios.get()
setProduct({
name: data.name,
desc: data.desc,
price: data.price,
offPrice: data.offPrice,
image: data.image,
feat: data.feat,
uid: data.uid,
date: data.date,
})
} catch (error) {
console.log(error)
}
}
fetchedProduct()
}, [productId])
return (
<div>
<form className="add-product-form" onSubmit={editProductSubmit}>
<div className="form-floating mb-3">
<input
defaultValue={product.name}
onChange={changeHandler}
type="text"
className="form-control"
id="productName"
name="name"
placeholder="نام محصول"
/>
<label className="form-label" htmlFor="productName">نام محصول</label>
</div>
<div className="form-floating mb-3">
<textarea
defaultValue={product.desc}
onChange={changeHandler}
className="form-control"
id="productDesc"
name="desc"
placeholder="Leave a comment here"
style={{height: 200}}
/>
<label htmlFor="productDesc">توضیحات محصول</label>
</div>
<div className="form-floating mb-3">
<input
defaultValue={product.price}
onChange={changeHandler}
type="number"
className="form-control"
id="productPrice"
name="price"
placeholder="قیمت اصلی محصول"
/>
<label className="form-label" htmlFor="productPrice">قیمت اصلی محصول</label>
</div>
<div className="form-floating mb-3">
<input
defaultValue={product.offPrice}
onChange={changeHandler}
type="number"
className="form-control"
id="productOffPrice"
name="offPrice"
placeholder="قیمت با تخفیف محصول"
/>
<label className="form-label" htmlFor="productOffPrice">قیمت با تخفیف محصول</label>
</div>
<div className="form-floating mb-3">
<input
defaultValue={product.image}
onChange={changeHandler}
type="text"
className="form-control"
id="productImage"
name="image"
placeholder="لینک تصویر محصول"
/>
<label className="form-label" htmlFor="productImage">لینک تصویر محصول</label>
</div>
<div className="form-check form-switch form-check-reverse mb-3">
<input
onChange={switchHandler}
defaultChecked={product.feat}
value={switchVal}
type="checkbox"
className="form-check-input"
id="productFeat"
role="switch"
/> {/* This is the switch in question */}
<label className="form-check-label" htmlFor="productFeat">محصول ویژه است</label>
</div>
<div className="mb-3">
<button type="submit" className="btn btn-success">ویرایش محصول</button>
</div>
</form>
</div>
);
}
export default EditProductForm;
>Solution :
I cannot comment (since SO requieres a certain mount of reputation to comment :/), so i cannot answer the question you left on Tim van Dam’s answer directly. But if you want to have a conditional default value for a react components you probably would want to pass that conditional default value as a prop, then use THAT value to initialize your useState()