Right now I am using useEffect()
to check for the product via the id
param. And I can set initialState
value to an empty string, but is there any way that we can set the initialState
value via the backend or after the useEffect()
has been done.
If I am using value={product.name}
in <input/>
then the value is there in the Input field, but it never changes.
Please Help.
const reducer = (state, { type, payload }) => {
switch (type) {
case "name":
return { ...state, name: { value: payload } };
case "desc":
return { ...state, desc: { value: payload } };
case "price":
return { ...state, price: { value: payload } };
case "img":
return { ...state, img: { value: payload } };
case "isFeatured":
return { ...state, isFeatured: { value: payload } };
default:
return { state };
}
};
const initialState = {
name: { value: "" },
desc: { value: "" },
price: { value: "" },
img: { value: "" },
isFeatured: { value: false },
};
Product Component
const [state, dispatch] = useReducer(reducer, initialState);
const [showForm, setShowForm] = useState(false); const [error, setError] = useState("Invalid"); const [product, setProduct] = useState(); const id = useParams().id; useEffect(() => {
const fetchHandler = async () => {
await fetch(`http://localhost:5000/products/${id}`)
.then(async (res) => await res.json())
.then((p) => setProduct(p.product))
.catch((err) => setError(err));
};
fetchHandler(); }, [id]);
InputChangeHandler
const inputChangeHandler = (e, name) => {
switch (name.toString()) {
case "name":
dispatch({ type: "name", payload: e.target.value });
break;
case "price":
dispatch({ type: "price", payload: e.target.value });
break;
case "desc":
dispatch({ type: "desc", payload: e.target.value });
break;
case "img":
dispatch({ type: "img", payload: e.target.value });
break;
case "isFeatured":
dispatch({ type: "isFeatured", payload: Boolean(e.target.checked) });
break;
default:
break;
}
};
My Form
{showForm && (
<form onSubmit={submitForm}>
<FormControl>
<TextField
placeholder={product.name}
value={product.name}
onChange={(e) => inputChangeHandler(e, "name")}
margin="dense"
className="diff"
id="outlined-basic"
label="Name"
variant="outlined"
/>
<TextField
multiline
placeholder={product.description}
value={state.desc.value}
onChange={(e) => inputChangeHandler(e, "desc")}
id="outlined-basic"
margin="dense"
label="Description"
variant="outlined"
/>
<TextField
placeholder={product.price}
value={state.price.value}
onChange={(e) => inputChangeHandler(e, "price")}
margin="dense"
id="outlined-basic"
label="Price"
variant="outlined"
/>
<TextField
placeholder={product.image}
value={product.image}
onChange={(e) => inputChangeHandler(e, "img")}
margin="dense"
id="outlined-basic"
label="Image"
variant="outlined"
/>
<Checkbox
value={state.isFeatured.value}
onChange={(e) => inputChangeHandler(e, "isFeatured")}
name="checkedB"
color="primary"
/>
<Button type="submit"> Update</Button>
<FormHelperText id="my-helper-text">Update</FormHelperText>
</FormControl>
</form>
I can think of two options when dealing with async data in combination with a form in your situation.
The first option is to load the data
before rendering the form component. Then pass the data down to the form component as the initialState
. The drawback of this solution is that the form is completely hidden while loading the data.
The second option is to add an action to your reducer which updates more than a single field. You can dispatch this action after loading the product data and update all fields according to the updated data.
You can simplify the reducer by adding the field name to the payload.
const reducer = (state, { type, payload }) => {
switch (type) {
case "change":
return { ...state, [payload.name]: { value: payload.value } };
case "productLoaded":
return { ...state, ...payload };
default:
return { state };
}
};
const inputChangeHandler = (e, name) => {
const value = e.target.type === 'checkbox' ? Boolean(e.target.checked) : e.target.value;
dispatch({ type: "change", payload: { name, value } });
};
useEffect(() => {
const fetchHandler = async () => {
await fetch(`http://localhost:5000/products/${id}`)
.then(async (res) => await res.json())
.then((p) => {
setProduct(p.product);
dispatch({ type: "productLoaded", payload: p.product });
})
.catch((err) => setError(err));
};
fetchHandler();
}, [id]);