Unable to set property of a single element in an array - javascript

In an array named admin, I want to show a div on the click of a button "update" when when I do so, the div shows below all the elements of the array. I only want it to show below the selected element.
function Admin(props) {
const [showMe, setShowMe] = React.useState(false);
const [updateName, setupdateName] = React.useState("");
const [updateDesc, setupdateDesc] = React.useState("");
return (
<div>
<div className="adminProducts">
{props.admin.map((x, i) => (
<div>
{showMe ? (
<div className="UpdateSection">
<input
type="text"
placeholder="Product Name"
onChange={e => setupdateName(e.target.value)}
value={updateName}
/>
<br />
<textarea
placeholder="Product Description"
onChange={e => setupdateDesc(e.target.value)}
value={updateDesc}
/>
<button
type="submit"
onClick={e => {
props.UpdateInfo({ updateName, updateDesc }, { x }, i);
setupdateName("");
setupdateDesc("");
}}
>
Save
</button>
</div>
) : null}
<div>{x.name}</div>
<div>
<button onClick={e => setShowMe(!showMe)}>
{showMe ? "Close" : "Update"}
</button>
</div>
</div>
))}
</div>
</div>
);
}
I want to set showMe as true for individual elements in array so that the div with classname UpdateSection only shows for that specific element and not for any other element.

You can save the id of the element you want to be shown:
const [showMe, setShowMe] = React.useState([]);
// ...
const isShown = el => showMe.includes(el.id);
const toggleShown = el => {
setShowMe(shown => {
if (shown.includes(el.id)) {
return shown.filter(id => id !== el.id);
}
return [...shown, el.id];
});
};
//...
return (
<div>
<div className="adminProducts">
{props.admin.map((x, i) => (
<div>
{isShown(x) ? (
//...
<div>
<button onClick={e => toggleShown(x)}>
{isShown(x) ? "Close" : "Update"}
</button>
</div>
</div>
))}
</div>
</div>
);

Related

I have input a unique "key" prop but still got error asking me to input it in React

so this is my render, it render basically a ui where a username and their email. it also have the option to add new user but when I run the code in my local host, i get this error in my console: Warning: Each child in a list should have a unique "key" prop.
render() {
return (
<div className="App">
{this.state.users.map((user) => {
return (
<React.Fragment>
<div key={user._id} className="box">
<h3>{user.name}</h3>
<h4>{user.email}</h4>
<button
onClick={() => {
this.beginEdit(user);
}}
>
Update
</button>
<button
onClick={() => {
this.deleteUser(user);
}}
>
Delete
</button>
</div>
</React.Fragment>
);
})}
{this.renderAddUser()}
</div>
);
}
}
this is my AddRenderUser
// render new User
renderAddUser() {
return (
<React.Fragment >
<input
type="text"
placeholder="User name"
value={this.state.newUserName}
onChange={this.updateFormField}
name="newUserName"
/>
<input
type="text"
placeholder="User email"
value={this.state.newUserEmail}
onChange={this.updateFormField}
name="newUserEmail"
/>
<button onClick={this.addUser}>Add</button>
</React.Fragment>
);
}
Although I did put the keys props in my div, so not sure how to correct this error
Your key should be in your top-most element
render() {
return (
<div className="App">
{this.state.users.map((user) => {
return (
<React.Fragment key={user._id}>
<div className="box">
<h3>{user.name}</h3>
<h4>{user.email}</h4>
<button
onClick={() => {
this.beginEdit(user);
}}
>
Update
</button>
<button
onClick={() => {
this.deleteUser(user);
}}
>
Delete
</button>
</div>
</React.Fragment>
);
})}
{this.renderAddUser()}
</div>
);
}
}
You need to add the key prop to the React.Fragment tag like this:
<React.Fragment key={user._id}>
Or please try to use index value instead of user.id as follows:
render() {
return (
<div className="App">
{this.state.users.map((user, i) => {
return (
<React.Fragment key={i}>
<div className="box">
<h3>{user.name}</h3>
<h4>{user.email}</h4>
<button
onClick={() => {
this.beginEdit(user);
}}
>
Update
</button>
<button
onClick={() => {
this.deleteUser(user);
}}
>
Delete
</button>
</div>
</React.Fragment>
);
})}
{this.renderAddUser()}
</div>
);
}

After removing the image cannot input same image again

I created an image upload, and remove the uploaded image. Once upload the image we can remove it again and input another image. But I can't input the same image agin.
const [image, setImage] = useState<any>("noImage");
const [isImageUploaded, setIsImageUploaded] = useState<boolean>(false);
const handleImageChange = (event: any) => {
setImage(URL.createObjectURL(event.target.files[0]));
setIsImageUploaded(true);
};
const handleOnImageRemoveClick = () => {
setIsImageUploaded(false);
setImage("noImage");
};
<span>
<input type="file" className="d-none" onChange={handleImageChange} disabled={isImageUploaded} />
{isImageUploaded ? (
<div>
<div className="d-flex justify-content-center">
<Button variant="warning" onClick={handleOnImageRemoveClick}>
Remove Image
</Button>
</div>
</div>
) : (
<div>
<p>Click to upload the image</p>
</div>
)}
</span>
You are not emptying correctly your input. I would recommend you to use a ref with useRef. Also you can shorten your code by only have image state, like so:
const [image, setImage] = useState<any>("noImage");
const inputRef = useRef<any>(null); // import useRef from react
const handleImageChange = (event: any) => {
setImage(URL.createObjectURL(event.target.files[0]));
};
const handleOnImageRemoveClick = () => {
setImage("noImage");
inputRef.current.value="";
};
<span>
<input ref={inputRef} type="file" className="d-none" onChange={handleImageChange} disabled={image !== "noImage"} />
{image !== "noImage" ? (
<div>
<div className="d-flex justify-content-center">
<Button variant="warning" onClick={handleOnImageRemoveClick}>
Remove Image
</Button>
</div>
</div>
) : (
<div>
<p>Click to upload the image</p>
</div>
)}
</span>
I think you should reset currentTarget.value like this.
<input
...
onClick={(event)=> {
event.currentTarget.value = null
}}
/>
// TS
onClick={(event) => {
(event.target as HTMLInputElement).value = '';
}}

Dynamic Form in react with dynamic fields

I did looking for but i did not find an answer for my problem.
I need to create a dynamic form with dynamic fields, with the order that the user need.
import { useState } from "react";
const DemoLateral = () => {
const itemDataObject = {
title_item_lateral: '' ,
text_item_lateral: [],
image_lateral: [
{
title_image_lateral: '',
path_image_lateral: '',
}
],
document_lateral: [],
links: [
{
title_link:'' ,
link: ''
}
]
};
const addFields = () => {
let newItemField;
newItemField = itemDataObject;
setItems([...items, newItemField]);
};
const [items, setItems] = useState([]);
const [select, setSelect] = useState([]);
console.log(items);
console.log('select: ', select);
const handleChange = () => {
//let index =
//let name = items[i][e.target.name]=[e.target.value];
console.log();
};
const submitForm = (e) => {
e.preventDefault();
};
console.log(select);
return (
<>
<h3 className="ms-5 mb-5"> AÑADIR ITEMS </h3>
<div className="container">
<form onSubmit={submitForm} className=''>
<div>
{items.map((input, i)=> (
<>
<div className="row align-items-center row mb-4" key={i}>
<label htmlFor="exampleFormControlSelect1">Selecciona el Campo</label>
<div className="col-2" key={i}>
<select className="form-control" id="exampleFormControlSelect1"
onChange={(e) => setSelect([select[i]=e.target.value])} key={i}>
<option>Subtitulo</option>
<option>Imagen</option>
<option>Link</option>
<option>Texto</option>
</select>
</div>
<div className='col-8'>
<input
placeholder="desde From"
id={i}
className='form-control'
value={select[i]}
onChange= {handleChange(i)}
type="text"
required
/>
</div>
<button className="btn btn-danger col-1" >Borrar</button>
</div>
</>
))
}
</div>
<button className="btn btn-success me-4 mt-5" type='submit'>AddSubmit</button>
</form>
<div className="mt-5 text-center">
<button className="btn btn-primary me-4 mb-4" value='items' onClick={addFields}>Add Items</button>
</div>
</div>
</>
);
};
export default DemoLateral;
with this code i try to create a dynamic form with fields, that would be set in the form like the user need:
p.e:
'subtitle'
'image'
'text'
'text'
'link'
'image'
for this i create a select to choose the type of field, and try to put the select in the attribute name, for then when submit all works.
But i can not achieve. :-(
Where is my wrong....
maybe there are other way to do the same kind of form?

Modal dialog displays from all elements of mapped array. How to select each item by ts and react js?

This code:
How to display a dialog when a button is clicked using react and typescript?
I wanna open dialog from each todos, how to make it ? I used react js and typescript. Help me to resolve this problem.
interface ListProps {
todos: INote[];
onDelete: (title: string) => void;
}
const TodoList: React.FunctionComponent<ListProps> = ({ todos, onDelete }) => {
const [showAlert, setShowAlert] = useState(false);
const [todo, setTodos] = useState(null);
How to select each item by ts?It doesn't work. What is reason? Thanks!
const handleOpenDialog = (todos: any) => {
setTodos(todos);
setShowAlert(true);
};
const handleCloseDialog = () => {
setShowAlert(false);
};
return (
<>
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
{showAlert && todo && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</div>
))}
</section>
</>
);
};
export default TodoList;
just add a condition to only show the AlertDialog on selected todos
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
{showAlert && todos.title===todo?.title && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</div>
))}
</section>
or just move the AlertDialog outside the map
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
</div>
))}
{showAlert && todo && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</section>

React trigger only one element in array

I am in the process of making a comment system like the one on youtube. In my implementation, when I click on modify, all comments are now inputs but only the value of the selected input will be modified. how to trigger only the element i clicked.
as you can see it triggers all the array elements
function App() {
const [open, setOpen] = useState(false);
return (
<div className="container mt-5">
<MDBRow>
{data &&
data.map((item) => (
<MDBCol md="7" lg="7" key={item.id} className="mb-4">
{!open && (
<>
<div className="font-weight-bolder float-left pr-2">
{item.name}
</div>
<div className="float-right pr-2">
<button
onClick={() => {
setOpen(true);
}}
>
Modifier
</button>
</div>
</>
)}
{open && (
<UpdateData
id={item.id}
name={item.name}
onAbort={() => setOpen(false)}
submit={() => setOpen(false)}
/>
)}
</MDBCol>
))}
</MDBRow>
</div>
);
}
export const UpdateData = ({ name, id, onAbort, submit }) => {
const formik = useFormik({
initialValues: {
id: id,
name: name,
},
onSubmit: async (values) => {
console.log(values);
submit();
},
});
return (
<form onSubmit={formik.handleSubmit}>
<MDBInput
value={formik.values.name}
name="name"
onChange={formik.handleChange}
/>
<div className="float-right">
<span onClick={onAbort} className="text-capitalize grey-text">
Cancel
</span>
<button type="submit">confirm</button>
</div>
</form>
);
};
And this is the sandbox
that i have created
To trigger only one element to be clicked you have to pass the index
function App() {
const [open, setOpen] = useState(false);
const [selectedRow, setSelectedRow] = useState(undefined);
const onSelectedRow = (index) => {
setSelectedRow(index);
setOpen(true);
}
return (
<div className="container mt-5">
<MDBRow>
{data &&
// here you will get the index
data.map((item,index) => (
<MDBCol md="7" lg="7" key={item.id} className="mb-4">
{!open && (
<>
<div className="font-weight-bolder float-left pr-2">
{item.name}
</div>
<div className="float-right pr-2">
// Now onClick pass the index of selected row to onSelectedRow
<button
onClick={() =>onSelectedRow(index)}
>
Modifier
</button>
</div>
</>
)}
// here add condition to open selected row
{ (open === true && selectedRow === index) ? (
<UpdateData
id={item.id}
name={item.name}
onAbort={() => setOpen(false)}
submit={() => setOpen(false)}
/>
) : null
}
</MDBCol>
))}
</MDBRow>
</div>
);
}
Sandbox code https://codesandbox.io/s/youthful-wave-k4eih?file=/src/App.js
If you have any queries comment below!
instead of having a false default value in your hook you should have a unique key for each element. By default, it applies to all elements.

Categories

Resources