I am stuck on editing user details and updating it.
What I'm trying to accomplish is when I click on "update" button, I can edit their name and email. And I can also delete the entry by clicking the "delete" button.
I have added the ability to add new User which I am able to code.
This is my code: https://codesandbox.io/s/zen-browser-5ifrel?file=/src/User.js
How do I add a if-else statement in my render so that IF i click on "update" button on one of the entry (e.g., id === selected.id), it will transform the "name" and "email" to textboxes to allow for edit. And 2 buttons for them to "confirm" their update or "cancel" their update.
render() {
return (
<div className="App">
{
this.state.users.map((u) => {
return (
<React.Fragment key={u._id}>
<div className="box">
<h3>{u.name}</h3>
<h4>{u.email}</h4>
<button onClick={() => {
this.beginEdit(u);
}}
>Update
</button>
<button onClick={() => {
this.deleteUser(u);
}}
>Delete
</button>
</div>
</React.Fragment>
);
})}
{this.renderAddUser()}
</div>
);
}
Use conditional rendering.
render() {
return (
<div className="App">
{
this.state.users.map((u) => {
return (
<React.Fragment key={u._id}>
<div className="box">
{u.isEditing ? (
<React.Fragment>
<input type="text" placeholder="name" />
<input type="text" placeholder="email" />
</React.Fragment>
) : (
<React.Fragment>
<h3>{u.name}</h3>
<h4>{u.email}</h4>
</React.Fragment>
)}
<button onClick={() => {
this.beginEdit(u);
}}
>Update
</button>
<button onClick={() => {
this.deleteUser(u);
}}
>Delete
</button>
</div>
</React.Fragment>
);
})}
{this.renderAddUser()}
</div>
);
}
You can probably figure out the rest.
you can use this that render output depends on the condition of (id === selectedUser.id)
state = {
selected = null;
}
render() {
return (
<div className="App">
{this.state.users.map((user) => {
return (
<div className="box" >
{selected?.id === user?._id ? (
<>
<input type="text" placeholder="name" defaultValue={user.name} />
<input type="text" placeholder="email" defaultValue={user.email} />
<button onClick={() => { this.handleCancel(user);}}>
Cancel
</button>
<button onClick={() => { this.handleConfirm(user);}}>
Confirm
</button>
</>
) : (
<>
<h3>{user.name}</h3>
<h4>{user.email}</h4>
<button onClick={() => { this.beginEdit(user);}}>
Update
</button>
<button onClick={() => { this.deleteUser(user);}}>
Delete
</button>
</>
)}
</div>
);
})}
{this.renderAddUser()}
</div>
);
}
Related
Consider the code below:
function Item({name, _key})
{
console.log('rendering Item')
const [updatingName, setUpdatingName] = useState(false);
const nameInputElement = useRef();
useEffect(() => {
if (updatingName) {
nameInputElement.current.focus();
}
}, [updatingName]);
function onUpdateClick() {
setUpdatingName(true);
}
function onCancelClick() {
setUpdatingName(false);
}
return (
<div>
<input ref={nameInputElement} type="text" defaultValue={name} name="name"
disabled={!updatingName} />
{!updatingName
? <>
<button key={1} type="button" onClick={onUpdateClick}>Update</button>
<button key={2} type="submit" name="delete" value={_key}>Remove</button>
</>
: <>
<button key={3} type="submit" name="update" onClick={(e) => {setUpdatingName(false)}}>Save</button>
<button key={4} type="button" onClick={onCancelClick}>Cancel</button>
</>}
</div>
)
}
function ItemList({title})
{
return <>
<h1>{title}</h1>
<form method="post" onSubmit={(e) => {console.log('submitting');e.preventDefault()}}>
<Item name={'small'} _key={0} />
</form>
</>
}
export default ItemList;
The problem that I am facing is the click of the Save button. When it's clicked, as you can see, I trigger a state change. But at the same time, I also want the button to cause the underlying <form>'s submission.
(To check whether the form is submitted, I've prevented its default submit mechanism and instead gone with a simple log.)
However, it seems to be the case that when the state change is performed from within the onClick handler of the Save button, it ceases to submit the form. If I remove the state change from within the handler, it then does submit the form.
Why is this happening?
Live CodeSandbox demo
When you call setUpdatingName(false) in save button's click handler, the button is removed from the DOM before submitting. You can add the logic for showing the buttons in ItemList, like below:
function ItemList({ title }) {
const [updatingName, setUpdatingName] = useState(false);
return (
<>
<h1>{title}</h1>
<form
method="post"
onSubmit={(e) => {
e.preventDefault();
setUpdatingName(false);
console.log("submitting");
}}
>
<Item
name={"small"}
_key={0}
updatingName={updatingName}
setUpdatingName={setUpdatingName}
/>
</form>
</>
);
}
export default ItemList;
function Item({ name, _key, updatingName, setUpdatingName }) {
console.log("rendering Item");
const nameInputElement = useRef();
useEffect(() => {
if (updatingName) {
nameInputElement.current.focus();
}
}, [updatingName]);
function onUpdateClick() {
setUpdatingName(true);
}
function onCancelClick() {
setUpdatingName(false);
}
return (
<div>
<input
ref={nameInputElement}
type="text"
defaultValue={name}
name="name"
disabled={!updatingName}
/>
{!updatingName ? (
<>
<button key={1} type="button" onClick={onUpdateClick}>
Update
</button>
<button key={2} type="submit" name="delete" value={_key}>
Remove
</button>
</>
) : (
<>
<button key={3} type="submit" name="update">
Save
</button>
<button key={4} type="button" onClick={onCancelClick}>
Cancel
</button>
</>
)}
</div>
);
}
Also, you could use useTransition to ask React to delay the state update, so the submission happens first:
function Item({ name, _key }) {
console.log("rendering Item");
const [isPending, startTransition] = useTransition();
const [updatingName, setUpdatingName] = useState(false);
const nameInputElement = useRef();
useEffect(() => {
if (updatingName) {
nameInputElement.current.focus();
}
}, [updatingName]);
function onUpdateClick() {
setUpdatingName(true);
}
function onCancelClick() {
setUpdatingName(false);
}
return (
<div>
<input
ref={nameInputElement}
type="text"
defaultValue={name}
name="name"
disabled={!updatingName}
/>
{!updatingName ? (
<>
<button key={1} type="button" onClick={onUpdateClick}>
Update
</button>
<button key={2} type="submit" name="delete" value={_key}>
Remove
</button>
</>
) : (
<>
<button
key={3}
type="submit"
name="update"
onClick={(e) => {
startTransition(() => setUpdatingName(false));
}}
>
Save
</button>
<button key={4} type="button" onClick={onCancelClick}>
Cancel
</button>
</>
)}
</div>
);
}
I cant reset my form on submit or modal close event using formik. I tried using resetForm({}), resetForm(initialValues),resetForm('') nothing works for me.
RestaurantDetails.js
setting initial values
// setting formik initialValues
const setFromikInitialValue = (product_option_groups) => {
let updatedOption = product_option_groups;
product_option_groups.forEach((option, optionIndex) => {
if (option.minimum === 1 && option.maximum === 1) {
//If displaying radio buttons for modifier
//selected option is kept in a key selected_modifier
updatedOption[optionIndex] = {
...option,
selected_modifier: option.product_options[0].id,
};
} else {
//If displaying check box for modifier
//selected_modifier_count key is added to track checked box count
updatedOption[optionIndex] = {
...option,
selected_modifier_count: 0,
};
}
//For each check box selected key is added
//selected is true if check box is checked & if unchecked it is false
option.product_options.forEach((item, itemIndex) => {
updatedOption[optionIndex].product_options[itemIndex] = {
...item,
selected: false,
};
});
});
return updatedOption;
};
const initialValues = {
product_id: selectedFoodItem.id,
options: setFromikInitialValue(modifiersData),
};
render part of formik
{/* Modal */}
<div
className="modal fade dish-modal"
id={`dishModal-${selectedFoodItem.id}`}
tabIndex={-1}
role="dialog"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
data-backdrop="static"
data-keyboard="false"
>
<div className="modal-dialog" role="document">
<div className="modal-content">
<Formik
initialValues={initialValues}
enableReinitialize
//form submition
onSubmit={async (values, { setSubmitting, resetForm }) => {
console.log(values);
setSubmitting(false);
resetForm(initialValues);
window.$(`#dishModal-${selectedFoodItem.id}`).modal("hide");
}}
//yup form validation
validationSchema={Yup.object().shape({
options: Yup.array().of(
Yup.object().shape({
selected_modifier_count: Yup.number()
.min(
Yup.ref("minimum"),
"minimum number of modifier not choosed"
)
.max(
Yup.ref("maximum"),
"maximum number of modifier exeded"
),
})
),
})}
>
{({
isSubmitting,
setFieldValue,
values,
errors,
touched,
resetForm,
}) => (
<Form>
<div className="modal-body">
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
onClick={() => {
resetForm({});
}}
>
<span aria-hidden="true">×</span>
</button>
<div className="item-details">
<h4>{selectedFoodItem.name}</h4>
<h4>
{dollar}
{selectedFoodItem.price}
</h4>
</div>
<div className="modifiers">
{/* mapping modifier items */}
{values.options && values.options.length
? values.options.map((modifier, optionIndex) => (
<div
key={`${modifier.id}-${optionIndex}`}
className="modifiers"
>
<h5>{modifier.name}</h5>
<h6>
{modifier.minimum || modifier.maximum ? (
errors.options && touched.options ? (
errors.options[optionIndex] &&
touched.options[optionIndex] ? (
<span className="text-danger">
Minimum {modifier.minimum} Maximum{" "}
{modifier.maximum}
</span>
) : (
<span>
Minimum {modifier.minimum} Maximum{" "}
{modifier.maximum}
</span>
)
) : (
<span>
Minimum {modifier.minimum} Maximum{" "}
{modifier.maximum}
</span>
)
) : (
<span className="text-danger">
Minimum {modifier.minimum} Maximum{" "}
{modifier.maximum}
</span>
)}
{modifier.minimum > 0 ? (
<span className="badge badge-light float-right">
Required
</span>
) : null}
</h6>
<ul>
{modifier.minimum === 1 &&
modifier.maximum === 1
? modifier.product_options.map(
(item, itemIndex) => (
<li key={itemIndex}>
<div className="form-check">
<input
defaultChecked={
itemIndex === 0 ? true : false
}
className="form-check-input"
type="radio"
name="temp"
id={`exampleCheck${item.id}`}
onChange={(
event,
value = item.id
) => {
setFieldValue(
`options[${optionIndex}]`,
{
...modifier,
selected_modifier: value,
}
);
}}
/>
<label
className="form-check-label"
htmlFor={`exampleCheck${item.id}`}
>
{item.name}
<span>
{dollar}
{item.price}
</span>
</label>
</div>
</li>
)
)
: modifier.product_options.map(
(item, itemIndex) => (
<li key={`${item.id}-${itemIndex}`}>
<div className="form-check">
<input
type="checkbox"
className="form-check-input"
id={`exampleCheck${item.id}`}
onClick={() => {
if (!item.selected) {
setFieldValue(
`options[${optionIndex}]`,
{
...modifier,
selected_modifier_count:
modifier.selected_modifier_count
? modifier.selected_modifier_count +
1
: 1,
}
);
} else {
setFieldValue(
`options[${optionIndex}]`,
{
...modifier,
selected_modifier_count:
modifier.selected_modifier_count -
1,
}
);
}
setFieldValue(
`options[${optionIndex}][product_options][${itemIndex}]`,
{
...item,
selected: !item.selected,
}
);
}}
defaultChecked={
item.selected ? true : false
}
/>
<label
className="form-check-label"
htmlFor={`exampleCheck${item.id}`}
>
{item.name}
<span>
{dollar}
{item.price}
</span>
</label>
</div>
</li>
)
)}
</ul>
</div>
))
: null}
</div>
<button
type="submit"
className="btn btn-primary continue-btn"
// disabled={isSubmitting || !dirty}
>
Continue
</button>
</div>
</Form>
)}
</Formik>
</div>
</div>
</div>
{/* Modal end */}
I need to do a form in react.js but when I try to type something in the form field, It doesn't let me type anything. I create a function for the input e my form. what am i doing wrong? please help me. thanks.
renderInput(title, value, onChange, validateField, placeholder) {
return (
<div className="col-12 col-md-4">
<div className="form-groud">
<label>{title}</label>
<input type="text" className="form-control"
name={title}
value={value}
onChange={e => onChange(e)}
onBlur={validateField}
placeholder={placeholder} />
</div>
</div>
)
}
renderForm() {
return (
<div className="form">
<div className="row">
{this.renderInput("Avatar", this.state.member.avatar, e => this.updateField(e), this.validateField, "profile picture" )}
{this.renderInput("Name", this.state.member.name, e => this.updateField(e), this.validateField, "name" )}
{this.renderInput("Email", this.state.member.email, e => this.updateField(e), this.validateField, "email" )}
{this.renderInput("Project", this.state.member.project, e => this.updateField(e), this.validateField, "project" )}
{this.renderInput("Devices", this.state.member.devices, e => this.updateField(e), this.validateField, "devices" )}
{this.renderInput("MainStack", this.state.member.mainstack, e => this.updateField(e), this.validateField,"main stack" )}
</div>
<hr />
<div className="row">
<div className="col-12 d-flex justify-content-end">
<button className="btn btn-primary"
onClick={e => this.save(e)}>
Save
</button>
<button className="btn btn-secondary ml-2"
onClick={e => this.clear(e)}>
Cancel
</button>
</div>
</div>
</div>
)
}
My updateField method:
updateField (event) {
const member = {...this.state.member}
member[event.target.avatar] = event.target.value
member[event.target.name] = event.target.value
member[event.target.project] = event.target.value
member[event.target.devices] = event.target.value
member[event.target.mainstack] = event.target.value
this.setState({ member })
}
you should controll input form value's using react state, in React is called a “controlled component”. So check your updateField function and make sure that it change the state value.
https://reactjs.org/docs/forms.html
I have a form using Formik library, all are right but when i want to confirm and submit i need to pass first a modal, the problem is when i launch the confirm modal all data i put in the form are eliminate and nothing is send, i noticed when the i launch the modal, the form tag are upload and then lost the data, someone has the same problem ? o are something to prevent the form tag upload when change the state of the modal ?
here is part of my code, i use CSSTransition on the modal, that mount and unmount the component, and i call previous fields on the form using FieldArray
<Formik
enableReinitialize
initialValues={INITIAL_VALUES}
onSubmit={(values) => {
this.props.onSubmitForm(values.rows);
}}
>
{({ values, errors, touched, isValid }) => (
<Form>
<Title />
<div className="wrapper__brick wrapper__brick--to-up">
<FieldArray
name="rows"
render={({ push }) => (
<>
<div className="wrapper__block">
<div className="master-table">
<FieldArray
name="rows"
render={({ remove }) => (
<div className="master-table__container">
{values.rows.length > 0 &&
values.rows.map((rows, index) => (
<div
className="master-table__row"
key={index}
>
<div className="master-table__item">
<Field
className={errors.rows && errors.rows[index] && touched.rows && touched.rows[index] ?
'input input--empty is-danger'
:
'input input--empty'
}
name={`rows.${index}.name`}
placeholder="Name"
type="text"
validate={validateName}
/>
<ErrorMessage
name={`rows.${index}.name`}
component="p"
className="help is-danger"
/>
</div>
<div className="master-table__item">
<div className={rows.default ? 'select select--disabled' : 'select select--empty'}>
<Field
as="select"
disabled={rows.default}
name={`rows.${index}.kind`}
>
{this.allowedKinds.map((row) => (
<option
value={row.internal}
key={row.internal}
>
{row.name}
</option>
))}
</Field>
</div>
</div>
<div className="master-table__item">
<button
className="button button--trigger tooltip is-tooltip-right"
data-tooltip={rows.default ? t('common.deleteNotAvailable') : t('common.delete')}
onClick={() => remove(index)}
type="button"
disabled={rows.default}
>
<span className="icon">
<i className="far fa-trash-alt" />
</span>
</button>
</div>
</div>
))}
</div>
)}
/>
</div>
</div>
<Footer
onAddRow={() => push({
id: UUID.v4(),
name: '',
kind: 'string',
type: 'generic',
required: false,
default: false
})}
validForm={!isValid}
/>
<TransitionWrapper stateToggle={this.state.openModal}>
<ModalDialog
iconModal="fas fa-info-circle"
titleText={t('modals.descriptionContinue')}
onCloseMethod={this.handleShowModal}
onAcceptMethod={this.props.onSubmitForm}
/>
</TransitionWrapper>
</>
)}
/>
</div>
</Form>
)}
</Formik>
I'm trying to make an comment input from map,
but since I use the same useState all the input fields get changed.
How can I target a specific input?
return (
<div>
{posts.map(post => (
<div key={post.id}>
<img
src={`https://localhost:1111/api/posts/uploads/images/${post.content}`}
alt={`${post.id}`}
/>
<p>{post.description}</p>
<span>{post.likes ? post.likes : 0}</span>
<button onClick={() => like(post.id)}>Like</button>
<Link to={`/post/${post.id}`}>Edit</Link>
<button onClick={() => deletePost(post.id)}>Delete</button>
<form onSubmit={uploadComment}>
<input
type="text"
onChange={handleComment}
value={comment}
placeholder="Comment"
/>
</form>
</div>
))}
</div>
)
You have an own state per rendered post, which means that it is a use case for an own component:
function Post(post, deletePost) {
const [comment, setComment] = useState('');
const uploadComment = () => {}; // your code is missing
return (
<div key={post.id}>
<img
src={`https://localhost:1111/api/posts/uploads/images/${post.content}`}
alt={`${post.id}`}
/>
<p>{post.description}</p>
<span>{post.likes ? post.likes : 0}</span>
<button onClick={() => like(post.id)}>Like</button>
<Link to={`/post/${post.id}`}>Edit</Link>
<button onClick={() => deletePost(post.id)}>Delete</button>
<form onSubmit={uploadComment}>
<input
type="text"
onChange={e => setComment(e.target.value)}
value={comment}
placeholder="Comment"
/>
</form>
</div>
)
}
Then your render function would look like this:
return (
<div>
{posts.map(post => <Post post={post} deletePost={deletePost} />)}
</div>
)
Consider using react useState hook per input.