Editing data in redux, but leaving some values unchanged - reactjs

I have an Edit Form in which i can update an already existing element of specified ID.
Let's say im editing this character:
{
"name": "Walter White",
"birthday": "1963-01-07",
"img": "https://upload.wikimedia.org/wikipedia/en/0/03/Walter_White_S5B.png",
"status": "Alive",
"appereance": [1, 2, 3],
"id": 1
}
I want to change the name,birthday,image and status. And i already did that. But I want the apperance array to remain the same. Although, after updating the data, the apperance is being erased.
Does anyone know, what can I do so that the apperance array won't change?
My EditForm code:
const UserEditForm = () => {
let { id } = useParams();
const { user } = useSelector((state) => state.user);
const [state, setState] = useState({
name: "",
birthday: "",
img: "",
status: "",
// appereance: ?????
});
const [error, setError] = useState("");
let history = useHistory();
let dispatch = useDispatch();
const { name, birthday, img, status, apperance } = 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 = () => {
dispatch(updateUser(state, id));
history.push("/");
setError("");
};
return (
<div>
<Button
style={{ width: "100px", marginTop: "20px" }}
variant="contained"
color="secondary"
type="button"
onClick={() => history.push("/")}
>
Go Back
</Button>
<h2>Edit User</h2>
{error && <h3 style={{ color: "red" }}>{error}</h3>}
<form onSubmit={handleSubmit}>
<TextField
id="standard-basic"
label="Name"
value={name || ""}
name="name"
type="text"
required
onChange={handleInputChange}
/>
<br />
<TextField
id="standard-basic"
name="birthday"
value={birthday || ""}
type="date"
required
onChange={handleInputChange}
/>
<br />
<TextField
id="standard-basic"
label="img"
value={img || ""}
name="img"
type="url"
required
onChange={handleInputChange}
/>
<select
id="standard-basic"
label="status"
value={status || ""}
name="status"
onChange={handleInputChange}
>
<option value={""}>Brak informacji</option>
<option value={"Alive"}>Alive</option>
<option value={"Dead"}>Dead</option>
</select>
<Button
style={{ width: "100px" }}
variant="contained"
color="primary"
type="submit"
onChange={handleInputChange}
>
Update
</Button>
</form>
</div>
);
};
EDIT ADDED REDUCER
const fetchState = {
users: [],
user: {},
};
export const userReducer = (state = fetchState, { type, payload }) => {
switch (type) {
case USERS_FETCH:
return { ...state, users: payload };
case ADD_USER:
return { ...state, users: [...state.users, payload] };
case UPDATE_USER:
return { ...state, users: [...state.users, payload] };
case GET_SINGLE_USER:
return {
...state,
user: payload,
};
default:
return state;
}
};
export const selectedUserReducer = (state = {}, { type, payload }) => {
switch (type) {
case GET_USER:
return { ...state, ...payload };
default:
return state;
}
};

There might be another way to achieve the same result. I am posting mine.
You can alter your object in the reducer and store only the data which you want to.
eg:-
case UPDATE_USER: {
const updateState = {
...state.user.appearance,
name: payload.name,
birthday: payload.birthday,
img: payload.img,
status: payload.status,
id: payload.id
};
return {
...state,
updateState,
};
}
USER REDUCER
const fetchState = {
user: [
name: "",
birthday: "",
img: "",
status: "",
appereance: "",
]
}
Please try and let me know , cheers !

Related

add and remove index of form array similar to query builder

I have a form with three input box and a map method so when I enter to add it create the same form below the button when I remove the specific index then it removes the last index, not that specific one although it shows correct data in the console.
const [formData, setFormData] = useState({
wk: [
{
companyName: "",
reasonForChange: "",
role: ""
}
]
});
const [wkArray, setWkArray] = useState([1]);
you can test the whole code with this URL - https://codesandbox.io/s/angry-star-08hyke?file=/src/App.js
You had to bind the input values with the state so they can be updated when re-rendering according to the state change. So the input should look like:
<input
placeholder="company name"
value={item.companyName}
onChange={(e) => {
let temp = [...formData?.wk];
temp[index].companyName = e?.target?.value;
setFormData({ ...formData, wk: temp });
}}
/>
Here's the working code.
import "./styles.css";
import { useState } from "react";
export default function App() {
const [formData, setFormData] = useState({
wk: [
{
companyName: "",
reasonForChange: "",
role: ""
}
]
});
const [wkArray, setWkArray] = useState([1]);
return (
<>
{console.log(wkArray, formData)}
<button
onClick={() => {
setWkArray([...wkArray, Math.floor(Math.random() * 100)]);
setFormData({
...formData,
wk: [
...formData?.wk,
{
companyName: "",
role: "",
reasonForChange: ""
}
]
});
}}
>
Add
</button>
{formData.wk?.map((item, index) => {
return (
<div key={index} style={{ display: "flex" }}>
{formData.wk?.length >= 1 && (
<button
onClick={(e) => {
setWkArray(
wkArray.filter((_, i) => {
return i !== index;
})
);
setFormData({
wk: formData.wk.filter((_, i) => i !== index)
});
}}
>
X
</button>
)}
<div>
<input
placeholder="company name"
value={item.companyName}
onChange={(e) => {
let temp = [...formData?.wk];
temp[index].companyName = e?.target?.value;
setFormData({ ...formData, wk: temp });
}}
/>
</div>
<div>
<input
placeholder="reason for change"
value={item.reasonForChange}
onChange={(e) => {
let temp = [...formData?.wk];
temp[index].reasonForChange = e?.target?.value;
setFormData({ ...formData, wk: temp });
}}
/>
</div>
<div>
<input
placeholder="role"
value={item.role}
onChange={(e) => {
let temp = [...formData?.wk];
temp[index].role = e?.target?.value;
setFormData({ ...formData, wk: temp });
}}
/>
</div>
</div>
);
})}
</>
);
}
You can see the live demo here: https://codesandbox.io/s/amazing-fermat-jtpcli?file=/src/App.js
I have made some minor changes to improve the code.

How to use inline dispatch in useReducer Hook

Here I have a form and I am managing my state with the help of useReducer and on every key stock I want to change value in my form like we use to do in useState which is known as two way binding and while dispatching useReducer if I point to external change function like this:
const idHandler = (e) => {
dispatch({ type: "0", value: e.target.value });
}
onChange={idHandler}
it works but I want to use inline function to bind my values and the problem is like when we use external function we recieve a event through we can bind value like:
function (event){
event.target.value;
}
but here when I call dispatch inside inline anonymous function it doesnt recognize event (I want to send event.target.value as payload)
onChange={()=>dispatch({ type: "0", value: e.target.value })}
I also tried this.target.value but doesnt works so how am I supposed to deal with it:
import "./ProductForm.css";
import { useReducer } from "react";
const ProductForm = () => {
const initialState = {
product_id: "e11",
product_name: "Masti kr ryian",
product_quantity: 0,
product_description: "",
product_type: "",
product_valid: "false",
product_price: 0,
product_title: "",
product_image: "",
};
const reducer = (state, action) => {
if (action.type === "0") {
return { product_id: action.value };
} else if (action.type === "1") {
return state.product_name;
} else {
return "Error";
}
};
const [state, dispatch] = useReducer(reducer, initialState);
return (
<form>
<div className="form-group">
<label>Product id</label>
<input
type="text"
className="form-control"
value={state.product_id}
onChange={() => dispatch({ type: "0", val: event.target.value })}
/>
</div>
<div className="form-group">
<label>Product Name</label>
<input
type="text"
className="form-control"
value={state.product_name}
// onChange={(e) => dispatch("1")}
/>
</div>
<button type="submit" className="btn btn-primary mt-4">
Submit
</button>
</form>
);
};
export default ProductForm;
Because you never declared the e parameter
onChange={e => dispatch({ type: "0", value: e.target.value })}

in Reactjs, this.props loses fields which don't belong to the table on which data added or edited

I'm new to ReactJs. I've an getResorts API which returns
data(resortId,name,address,phone,details,placeId,city,state,country) from
1)resort(resortId,name,address,phone,details,placeId) and
2) place table(placeId,city,state,country)
This data from API, I'm binding it to a grid as shown here
I've a separate API to get places data only from places table to bind the dropdowns -city,state and country in modal dialog for add/edit option and pass the selected city Id as the placeId while adding or editing resort as shown here
In ComponentDidUpdate(), after a succesful add or edit of a resort, the this.props has all the resorts of bound to the grid but the record for the updated resort or newly added resort has the city,state and country parts missing even though the resort data(resortId,name,phone,details,placeId) has been successfully added or edited, so my grid look like this after an insert or edit city,state,country missing for record updated or added in this.props
This might be due to the city,state,country not being part of the resort table, how to fix this - the this.props after successful add or edit? How can we know the action status type of saga function successfully executed in ComponentDidUpdate?
CompnentDidUpdate in resort-list.js
componentDidUpdate(prevProps, prevState, snapshot) {
const { resorts } = this.props;
if (!isEmpty(resorts) && size(prevProps.resorts) !== size(resorts)) {
this.setState({ resorts: {}, isEdit: false });
}
}
componentDidMount() {
const { resorts, onGetResorts } = this.props;
const {places, onGetPlaces}=this.props;
if (resorts && !resorts.length) {
onGetResorts();
}
this.setState({ resorts });
if (places && !places.length) {
onGetPlaces();
}
this.setState({ places });
}
<div className="table-responsive">
<BootstrapTable
{...toolkitprops.baseProps}
{...paginationTableProps}
selectRow={selectRow}
defaultSorted={defaultSorted}
classes={
"table align-middle table-nowrap table-hover"
}
bordered={false}
striped={false}
responsive
ref={this.node}
/>
<Modal
isOpen={this.state.modal}
className={this.props.className}
>
<ModalHeader
toggle={this.toggle}
tag="h4"
>
{!!isEdit ? "Edit Resort" : "Add Resort"}
</ModalHeader>
<ModalBody>
<Formik
enableReinitialize={true}
initialValues={{
name:
(resort && resort.name) || "",
address:
(resort && resort.address) ||
"",
phone:
(resort && resort.phone) ||
"",
details:
(resort && resort.details) ||
"",
placeId: (resort && resort.placeId) ||
"",
city:
(resort && resort.city) || "",
state:
(resort && resort.state) || "",
country:
(resort && resort.country) ||
"",
}}
validationSchema={Yup.object().shape({
name: Yup.string().required(
"Please Enter Resort Name"
),
phone: Yup.number().required(
"Please Enter Phone"
),
address: Yup.string().required(
"Please Enter Address"
),
details: Yup.string().required(
"Please Enter Details"
),
/* tags:
Yup.array().required(
"Please Select Tags"
),*/
city:
Yup.string().required(
"Please Enter City"
),
state:
Yup.string().required(
"Please Enter State"
),
country:
Yup.string().required(
"Please Enter Country"
),
})}
onSubmit={values => {
console.log(">>>values>>>>> "+JSON.stringify(values));
if (isEdit) {
const updateResort = {
id: resort.id,
name: values.name,
address: values.address,
phone: values.phone,
details: values.details,
// placeId:updatePlaceId.placeId,
placeId:this.state.placeId,
city: values.city,
state: values.state,
country: values.country,
};
console.log("----update----- "+JSON.stringify(updateResort))
// update user
onUpdateResort(updateResort);
} else {
// const newPlaceId=places.filter(x=>x.city===values["city"]);
const newResort = {
// id: values["id"],
name: values["name"],
address: values["address"],
phone: values["phone"],
details: values["details"],
placeId:this.state.placeId,
// placeId:newPlaceId.placeId
city: values["city"],
state: values["state"],
country: values["country"],
};
console.log("-----new---- "+JSON.stringify(newResort))
// save new resort
onAddNewResort(newResort);
}
this.setState({ selectedResort: null });
this.toggle();
}}
>
{({ errors, status, touched,setFieldValue,handleChange}) => (
<Form>
<Row>
<Col className="col-12">
<div className="mb-3">
<Label className="form-label">
Resort Name
</Label>
<Field
name="name"
type="text"
className={
"form-control" +
(errors.name &&
touched.name
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
Address
</Label>
<Field
name="address"
type="text"
className={
"form-control" +
(errors.address &&
touched.address
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="address"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
Phone
</Label>
<Field
name="phone"
type="tel"
className={
"form-control" +
(errors.phone &&
touched.phone
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="phone"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
Details
</Label>
<Field
name="details"
type="text"
className={
"form-control" +
(errors.details &&
touched.details
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="details"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
City
</Label>
<Field
onChange={(e) => {
handleChange(e);
let selectedGeo=places.filter((x) => x.city === e.target.value)
if(selectedGeo.length>0)
{
this.setState({placeId:selectedGeo[0].id});
setFieldValue("state", selectedGeo[0].state)
setFieldValue("country", selectedGeo[0].country)
}
}}
type="text"
name="city"
list="datalistCity"
className={
"form-control" +
(errors.city &&
touched.city
? " is-invalid"
: "")
}
multiple={false}
/>
<datalist id="datalistCity">
{
places.map((place) => {
return (
<option
value={place.city}
key={place.id}
>
</option>
);
})}
</datalist>
<ErrorMessage
name="city"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
State
</Label>
<Field
name="state" disabled
type="text"
className="form-control"
/>
<ErrorMessage
name="state"
component="div"
className="invalid-feedback"
/>
</div>
<div className="mb-3">
<Label className="form-label">
Country
</Label>
<Field
type="text"
name="country"
disabled
className="form-control"
/>
<ErrorMessage
name="country"
component="div"
className="invalid-feedback"
/>
</div>
</Col>
</Row>
<Row>
<Col>
<div className="text-end">
<button
type="submit"
className="btn btn-success save-resort"
>
Save
</button>
</div>
</Col>
</Row>
</Form>
)}
</Formik>
</ModalBody>
</Modal>
</div>
ResortsList.propTypes = {
resorts: PropTypes.array,
places:PropTypes.array,
className: PropTypes.any,
onGetResorts: PropTypes.func,
onGetPlaces: PropTypes.func,
onAddNewResort: PropTypes.func,
onDeleteResort: PropTypes.func,
onUpdateResort: PropTypes.func,
};
const mapStateToProps = ({ resorts,places }) => ({
resorts: resorts.resorts,
places:places.places
});
const mapDispatchToProps = dispatch => ({
onGetResorts: () => dispatch(getResorts()),
onGetPlaces: () => store.dispatch(getPlaces()),
onAddNewResort: resort => dispatch(addNewResort(resort)),
onUpdateResort: resort => dispatch(updateResort(resort)),
onDeleteResort: resort => dispatch(deleteResort(resort)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(ResortsList));
In ResortSaga,
const getUserName = () => {
if (localStorage.getItem("authUser")) {
const obj = JSON.parse(localStorage.getItem("authUser"))
return obj;
}
}
function* fetchResorts() {
try {
let obj = getUserName();
const response = yield call(getResorts, obj.accessToken)
console.log("resort list ---- "+JSON.stringify(response))
yield put(getResortsSuccess(response))
} catch (error) {
yield put(getResortsFail(error))
}
}
function* fetchResortDetails({id}) {
try {
const obj = JSON.parse(localStorage.getItem("authUser"));
const response = yield call(getResortDetails,id, obj)
yield put(getResortDetailsSuccess(response))
} catch (error) {
yield put(getResortDetailsFail(error))
}
}
function* onAddNewResort({ payload: resort }) {
try {
let obj = getUserName();
const response = yield call(addNewResort, resort, obj.accessToken)
console.log("------- "+JSON.stringify(response))
yield put(addResortSuccess(response))
} catch (error) {
yield put(addResortFail(error))
}
}
function* onUpdateResort({ payload: resort }) {
try {
//const obj = JSON.parse(localStorage.getItem("authUser"));
console.log("Saga..... edit >>>. "+JSON.stringify(resort))
let obj = getUserName();
const response = yield call(updateResort, resort, obj.accessToken)
yield put(updateResortSuccess(response))
} catch (error) {
yield put(updateResortFail(error))
}
}
function* onDeleteResort({ payload: resort }) {
try {
let obj = getUserName();
console.log("Saga..... delete >>>. "+JSON.stringify(resort))
const response = yield call(deleteResort, resort, obj.accessToken)
yield put(deleteResortSuccess(response))
} catch (error) {
yield put(deleteResortFail(error))
}
}
function* resortsSaga() {
yield takeEvery(GET_RESORTS, fetchResorts)
yield takeEvery(GET_RESORT_DETAILS, fetchResortDetails)
yield takeEvery(ADD_NEW_RESORT, onAddNewResort)
yield takeEvery(UPDATE_RESORT, onUpdateResort)
yield takeEvery(DELETE_RESORT, onDeleteResort)
}
export default resortsSaga
In action.js
import {
GET_RESORT_DETAILS,
GET_RESORT_DETAILS_FAIL,
GET_RESORT_DETAILS_SUCCESS,
GET_RESORTS,
GET_RESORTS_FAIL,
GET_RESORTS_SUCCESS,
ADD_NEW_RESORT,
ADD_RESORT_SUCCESS,
ADD_RESORT_FAIL,
UPDATE_RESORT,
UPDATE_RESORT_SUCCESS,
UPDATE_RESORT_FAIL,
DELETE_RESORT,
DELETE_RESORT_SUCCESS,
DELETE_RESORT_FAIL,
} from "./actionTypes"
export const getResorts = () => ({
type: GET_RESORTS,
})
export const getResortsSuccess = resorts => ({
type: GET_RESORTS_SUCCESS,
payload: resorts,
})
export const getResortsFail = error => ({
type: GET_RESORTS_FAIL,
payload: error,
})
export const getResortDetails = id => ({
type: GET_RESORT_DETAILS,
id,
})
export const getResortDetailsSuccess = resortDetails => ({
type: GET_RESORT_DETAILS_SUCCESS,
payload: resortDetails,
})
export const getResortDetailsFail = error => ({
type: GET_RESORT_DETAILS_FAIL,
payload: error,
})
export const addNewResort = resort => ({
type: ADD_NEW_RESORT,
payload: resort,
})
export const addResortSuccess = resort => ({
type: ADD_RESORT_SUCCESS,
payload: resort,
})
export const addResortFail = error => ({
type: ADD_RESORT_FAIL,
payload: error,
})
export const updateResort = resort => ({
type: UPDATE_RESORT,
payload: resort,
})
export const updateResortSuccess = resort => ({
type: UPDATE_RESORT_SUCCESS,
payload: resort,
})
export const updateResortFail = error => ({
type: UPDATE_RESORT_FAIL,
payload: error,
})
export const deleteResort = resort => ({
type: DELETE_RESORT,
payload: resort,
})
export const deleteResortSuccess = resort => ({
type: DELETE_RESORT_SUCCESS,
payload: resort,
})
export const deleteResortFail = error => ({
type: DELETE_RESORT_FAIL,
payload: error,
})
In actionType.js
/* USERS */
export const GET_RESORTS = "GET_RESORTS"
export const GET_RESORTS_SUCCESS = "GET_RESORTS_SUCCESS"
export const GET_RESORTS_FAIL = "GET_RESORTS_FAIL"
/* USERS PROFILE */
export const GET_RESORT_DETAILS = "GET_RESORT_DETAILS"
export const GET_RESORT_DETAILS_SUCCESS = "GET_RESORT_DETAILS_SUCCESS"
export const GET_RESORT_DETAILS_FAIL = "GET_RESORT_DETAILS_FAIL"
export const ADD_NEW_RESORT = "ADD_NEW_RESORT"
export const ADD_RESORT_SUCCESS = "ADD_RESORT_SUCCESS"
export const ADD_RESORT_FAIL = "ADD_RESORT_FAIL"
export const UPDATE_RESORT = "UPDATE_RESORT"
export const UPDATE_RESORT_SUCCESS = "UPDATE_RESORT_SUCCESS"
export const UPDATE_RESORT_FAIL = "UPDATE_RESORT_FAIL"
export const DELETE_RESORT = "DELETE_RESORT"
export const DELETE_RESORT_SUCCESS = "DELETE_RESORT_SUCCESS"
export const DELETE_RESORT_FAIL = "DELETE_RESORT_FAIL"
In reducer.js
import {
GET_RESORT_DETAILS,
GET_RESORT_DETAILS_FAIL,
GET_RESORT_DETAILS_SUCCESS,
GET_RESORTS,
GET_RESORTS_FAIL,
GET_RESORTS_SUCCESS,
ADD_NEW_RESORT,
ADD_RESORT_SUCCESS,
ADD_RESORT_FAIL,
UPDATE_RESORT,
UPDATE_RESORT_SUCCESS,
UPDATE_RESORT_FAIL,
DELETE_RESORT,
DELETE_RESORT_SUCCESS,
DELETE_RESORT_FAIL,
} from "./actionTypes"
const INIT_STATE = {
resorts: [],
resortDetails: {},
error: {},
}
const resorts = (state = INIT_STATE, action) => {
switch (action.type) {
case GET_RESORTS_SUCCESS:
return {
...state,
resorts: action.payload,
}
case GET_RESORTS_FAIL:
return {
...state,
error: action.payload,
}
case ADD_RESORT_SUCCESS:
return {
...state,
resorts: [...state.resorts, action.payload],
}
case ADD_RESORT_FAIL:
return {
...state,
error: action.payload,
}
case GET_RESORT_DETAILS_SUCCESS:
return {
...state,
resortDetails: action.payload,
}
case GET_RESORT_DETAILS:
return {
...state,
resortDetails: action.payload,
}
case UPDATE_RESORT_SUCCESS:
return {
...state,
resorts: state.resorts.map(resort =>
resort.id.toString() === action.payload.id.toString()
? { resort, ...action.payload }
: resort
),
}
case UPDATE_RESORT_FAIL:
return {
...state,
error: action.payload,
}
case DELETE_RESORT_SUCCESS:
return {
...state,
resorts: state.resorts.filter(
resort => resort.id.toString() !== action.payload.id.toString()
),
}
case DELETE_RESORT_FAIL:
return {
...state,
error: action.payload,
}
case GET_RESORT_DETAILS_FAIL:
return {
...state,
error: action.payload,
}
default:
return state
}
}
export default resorts
So, how do I get type success status from saga for add/edit in ComponentDidMount, so I can add the missing city,state,country manually to the this.props for the resort that was just modified based on resortId? So, when data from this.props get rendered to the grid after add/edit, the places part of the data - city,state and country aren't missing for the resort added or modified from the this.props and the grid.
In Watch, resort before editin this.props and here in watch, city,state,country removed in this.props after successful edit

Not changing the specified data on update-action

I have an Edit Form in which i can update an already existing element of specified ID.
Let's say im editing this character:
{
"name": "Walter White",
"birthday": "1963-01-07",
"img": "https://upload.wikimedia.org/wikipedia/en/0/03/Walter_White_S5B.png",
"status": "Alive",
"appereance": [1, 2, 3],
"id": 1
}
I want to change the name,birthday,image and status. And i already did that. But I want the apperance array to remain the same. Although, after updating the data, the apperance is being erased.
Does anyone know, what can I do so that the apperance array won't change?
My EditForm code:
const UserEditForm = () => {
let { id } = useParams();
const { user } = useSelector((state) => state.user);
const [state, setState] = useState({
name: "",
birthday: "",
img: "",
status: "",
// appereance: ?????
});
const [error, setError] = useState("");
let history = useHistory();
let dispatch = useDispatch();
const { name, birthday, img, status, apperance } = 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 = () => {
dispatch(updateUser(state, id));
history.push("/");
setError("");
};
return (
<div>
<Button
style={{ width: "100px", marginTop: "20px" }}
variant="contained"
color="secondary"
type="button"
onClick={() => history.push("/")}
>
Go Back
</Button>
<h2>Edit User</h2>
{error && <h3 style={{ color: "red" }}>{error}</h3>}
<form onSubmit={handleSubmit}>
<TextField
id="standard-basic"
label="Name"
value={name || ""}
name="name"
type="text"
required
onChange={handleInputChange}
/>
<br />
<TextField
id="standard-basic"
name="birthday"
value={birthday || ""}
type="date"
required
onChange={handleInputChange}
/>
<br />
<TextField
id="standard-basic"
label="img"
value={img || ""}
name="img"
type="url"
required
onChange={handleInputChange}
/>
<select
id="standard-basic"
label="status"
value={status || ""}
name="status"
onChange={handleInputChange}
>
<option value={""}>Brak informacji</option>
<option value={"Alive"}>Alive</option>
<option value={"Dead"}>Dead</option>
</select>
<Button
style={{ width: "100px" }}
variant="contained"
color="primary"
type="submit"
onChange={handleInputChange}
>
Update
</Button>
</form>
</div>
);
};
EDIT ADDED REDUCER
const fetchState = {
users: [],
user: {},
};
export const userReducer = (state = fetchState, { type, payload }) => {
switch (type) {
case USERS_FETCH:
return { ...state, users: payload };
case ADD_USER:
return { ...state, users: [...state.users, payload] };
case UPDATE_USER:
return { ...state, users: [...state.users, payload] };
case GET_SINGLE_USER:
return {
...state,
user: payload,
};
default:
return state;
}
};
export const selectedUserReducer = (state = {}, { type, payload }) => {
switch (type) {
case GET_USER:
return { ...state, ...payload };
default:
return state;
}
};

React TypeScript: Multiple State updates but only first one gets applied

I made a sandBox here https://codesandbox.io/s/old-mountain-xl7nz when you don't full in any of the form inputs I expect to see 3 errors but I only get one. I don't understand why
import React, { ChangeEvent, useState } from 'react';
import { Link } from 'react-router-dom';
import loadingImg from '../images/loading.svg';
const Join: React.FC = () => {
const [state, setState] = useState({
email: '',
emailError: '',
fullName: '',
fullNameError: '',
loading: false,
password: '',
passwordError: '',
});
const {
email,
emailError,
fullName,
fullNameError,
password,
passwordError,
} = state;
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
event.persist();
setState((prev) => ({
...prev,
[event.target.id]: event.target.value,
}));
};
const onSubmit = (event: React.FormEvent) => {
event.preventDefault();
if (validate('fullName') && validate('email') && validate('password')) {
console.log('FIRE FORM');
}
};
const onBlur = (event: ChangeEvent<HTMLInputElement>) => {
validate(event.target.id);
};
const validate = (id: string) => {
switch (id) {
case 'fullName':
if (!/^.{6,7}$/.test(fullName)) {
setState((prev) => ({ ...prev, fullNameError: 'err' }));
return false;
} else {
setState((prev) => ({ ...prev, fullNameError: '' }));
return true;
}
break;
case 'email':
if (!/\S+#\S+\.\S+/.test(email)) {
setState((prev) => ({ ...prev, emailError: 'err' }));
return false;
} else {
setState((prev) => ({ ...prev, emailError: '' }));
return true;
}
break;
default:
if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/.test(password)) {
setState((prev) => ({ ...prev, passwordError: 'err' }));
return false;
} else {
setState((prev) => ({ ...prev, passwordError: '' }));
return true;
}
}
};
return (
<div className='join'>
<h2>JOIN</h2>
<h3>some subheading</h3>
<form onSubmit={onSubmit}>
<label>Name</label>
<input
type='text'
placeholder='Full name'
id='fullName'
value={fullName}
onChange={onChange}
onBlur={onBlur}
/>
{fullNameError}
<label>Email</label>
<input
type='text'
placeholder='Email address'
id='email'
value={email}
onChange={onChange}
onBlur={onBlur}
/>
{emailError}
<label>Password</label>
<input
type='password'
placeholder='Create a password'
id='password'
value={password}
onChange={onChange}
onBlur={onBlur}
/>
{passwordError}
<button color='primary'>
{!state.loading ? (
'Join Now'
) : (
<img src={loadingImg} alt='loadingd' className='loading' />
)}
</button>
<div className='join--terms'>
By joining, you agree to our
<Link to={{ pathname: '/terms' }}> Terms of Service</Link> and
<Link to={{ pathname: '/terms' }}> Privacy Policy</Link>
</div>
</form>
</div>
);
};
export { Join };
your conditions are not evaluating when first condition is false...
so you better to do something like this....
const onSubmit = (event: React.FormEvent) => {
event.preventDefault();
const fullNameValidation = validate('fullName')
const emailValidation = validate('email')
const passwordValidation = validate('password')
if (fullNameValidation && emailValidation && passwordValidation) {
console.log('FIRE FORM');
}
};

Resources