After removing the image cannot input same image again - javascript

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 = '';
}}

Related

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?

React Hook useRef return style property as null

I am working on a modal in which I have to access the id property of an HTML element to change its display property. I have used the useRef Hook to attain it but got the following errors.
TypeError: Cannot read property 'style' of null
Modal.js:- The code is of 1000 Lines so I am sharing only the part where I have used it.
const [isAddProjectClicked, setisAddProjectClicked] = useState(true);
const [isAddProjectClicked, setisAddProjectClicked] = useState(true);
const addProjectModal = useRef(null);
const closeAddProjectModal = () => {
addProjectModal.current.style.display = 'block';
};
const renderAddProject = () => {
return (
<div>
<div className="task-accordion" ref={addProjectModal} style={{ display: 'none' }}>
<i
className="fa fa-close close-icon"
id="closeIconn"
onClick={() => closeAddProjectModal()}
aria-hidden="true"
></i>
<h4>ADD Project</h4>
<div className="form-group">
<textarea
id="textAreaAddProject"
type="text"
style={{ height: '30vh' }}
placeholder="Project Title"
name="Title"
className="form-control"
onChange={(e) => {
setprojectTitle(e.target.value);
}}
required
/>
</div>
<div className="col-sm-12 add-mupps-button">
<button type="submit" onClick={() => postProjectApiCall()}>
Save
</button>
</div>
</div>
</div>
);
};
<div className="col-sm-8 last-div padding-0">
{isAddProjectClicked && renderAddProject()}
</div>
You can change the visibility of your model by creating a state like this.
import { useState } from "react";
function App() {
const [isAddProjectClicked, setisAddProjectClicked] = useState(true);
const [addProjectModal, openAddProjectModal] = useState("none"); // creating the state
const renderAddProject = () => {
return (
<div>
<div className="task-accordion" style={{ display: addProjectModal }}> {/* Set that as display's property value */}
<i
className="fa fa-close close-icon"
id="closeIconn"
onClick={() => openAddProjectModal("block")}
aria-hidden="true"
></i>
<h4>ADD Project</h4>
<div className="form-group">
<textarea
id="textAreaAddProject"
type="text"
style={{ height: "30vh" }}
placeholder="Project Title"
name="Title"
className="form-control"
required
/>
</div>
<div className="col-sm-12 add-mupps-button">
<button type="submit">Save</button>
</div>
</div>
</div>
);
};
return (
<>
{isAddProjectClicked && renderAddProject()}
<button onClick={() => openAddProjectModal("block")}>Click Me</button>
{/* Change the state using any event you want */}
</>
);
}
export default App;
Let me know if you need further support.
If the component is not mounted, the ref is null so you should add condition to it
const closeAddProjectModal = () => {
if(addProjectModal.current) {
addProjectModal.current.style.display = 'block';
}
};
or just use optional chaining
const closeAddProjectModal = () => {
addProjectModal.current?.style.display = 'block';
};
Use react to set the style:
function AddProjectModal(props) {
const [visible, setVisible] = useState(visible);
return <div style={{display: visible?'block':'none'}}>
<button onClick={()=>setVisible(false)}>
Close
</button>
</div>
}
No need for a ref to change the style or a class

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>

How can I pass a data to another component from a list with React?

I got stuck in a part of my code. Basically I have a list and I would like to show more info when I click on item, I have a modal and I would like to show this details on a modal. I managed to pass some information, however, when I pass the array it displays only the last item in the list and I would like to display exactly the one I clicked.
**MODAL COMPONENT**
const Modal = ({ object, modal, setModal }) => {
if (modal)
return (
<div className="modal">
<div className="modal-body">
<div className="modal-header">
<span className="modal-title">{object.title}</span>
<span className="modal-date">xxxxx</span>
</div>
<div className="modal-content">
<div>
<span className="sinopse-style">Overview</span>
<hr></hr>
<p className="modal-description">xxx</p>
</div>
</div>
<div className="modal-footer">
<div className="tags">
<p className="tag-style">Ação</p>
<p className="tag-style">Aventura</p>
<p className="tag-style">Fantasia</p>
</div>
<div className="match-background">
<span className="match-title">75%</span>
</div>
</div>
<h1 onClick={() => setModal(false)}>Close Modal</h1>
</div>
</div>
);
return null;
};
export default Modal;
**RESULTS COMPONENT**
const Movies = (props) => {
const [data, setData] = React.useState("");
const [modal, setModal] = React.useState(false);
const [currentPage, setCurrentPage] = React.useState(1);
const [dataPerPage, setDataPerPage] = React.useState(5);
React.useEffect(() => {
const fetchData = async () => {
await fetch(API).then((response) =>
response
.json()
.then((json) => setData(json))
.catch((error) => console.log(error))
);
};
fetchData();
}, []);
function openModal() {
!modal ? setModal(true) : setModal(false);
}
// Get data per page
const indexLastData = currentPage * dataPerPage;
const indexFirstData = indexLastData - dataPerPage;
const currentData = data && data.results.slice(indexFirstData, indexLastData);
// console.log("current: " + data && currentData);
const paginate = (pageNumber) => setCurrentPage(pageNumber);
return (
<>
{data &&
currentData.map((object) => (
<div className="movies-container" key={object.id} onClick={openModal}>
<div>
<img
className="movie-billboard"
src={`https://image.tmdb.org/t/p/w185${object.poster_path}`}
/>
</div>
<div className="rightSideMovie">
<div className="header-movie-title">
<span className="movie-title">{object.title}</span>
</div>
<div className="match">
<span className="match-percentual">
{object.vote_average * 10}%
</span>
</div>
<span className="release-date">{object.release_date}</span>
<div className="movie-description">
<span>{object.overview}</span>
</div>
</div>
<Modal modal={modal} object={object} setModal={setModal} />
</div>
))}
<Pagination
dataPerPage={dataPerPage}
totalData={data && data.results.length}
paginate={paginate}
/>
</>
);
};
You could tweak your code a bit to select the movie from the list and only have 1 modal. Right now just the last modal will be there since you are using state outside the map.
**MODAL COMPONENT**
// No need to pass in modal -- just pass in the selectedMovie here because that is what you care about
const Modal = ({ selectedMovie, hideModal }) => {
if (selectedMovie)
return (
<div className="modal">
<div className="modal-body">
<div className="modal-header">
<span className="modal-title">{selectedMovie.title}</span>
<span className="modal-date">xxxxx</span>
</div>
<div className="modal-content">
<div>
<span className="sinopse-style">Overview</span>
<hr></hr>
<p className="modal-description">xxx</p>
</div>
</div>
<div className="modal-footer">
<div className="tags">
<p className="tag-style">Ação</p>
<p className="tag-style">Aventura</p>
<p className="tag-style">Fantasia</p>
</div>
<div className="match-background">
<span className="match-title">75%</span>
</div>
</div>
<h1 onClick={hideModal}>Close Modal</h1>
</div>
</div>
);
return null;
};
export default Modal;
**RESULTS COMPONENT**
const Movies = (props) => {
const [data, setData] = React.useState("");
// Just store the selected movie here
const [selectedMovie, setSelectedMovie] = useState(null);
const [currentPage, setCurrentPage] = React.useState(1);
const [dataPerPage, setDataPerPage] = React.useState(5);
React.useEffect(() => {
const fetchData = async () => {
await fetch(API).then((response) =>
response
.json()
.then((json) => setData(json))
.catch((error) => console.log(error))
);
};
fetchData();
}, []);
// Get data per page
const indexLastData = currentPage * dataPerPage;
const indexFirstData = indexLastData - dataPerPage;
const currentData = data && data.results.slice(indexFirstData, indexLastData);
// console.log("current: " + data && currentData);
const paginate = (pageNumber) => setCurrentPage(pageNumber);
return (
<>
{data &&
currentData.map((object) => (
<div className="movies-container" key={object.id} onClick={() => setSelectedMovie(object)}>
<div>
<img
className="movie-billboard"
src={`https://image.tmdb.org/t/p/w185${object.poster_path}`}
/>
</div>
<div className="rightSideMovie">
<div className="header-movie-title">
<span className="movie-title">{object.title}</span>
</div>
<div className="match">
<span className="match-percentual">
{object.vote_average * 10}%
</span>
</div>
<span className="release-date">{object.release_date}</span>
<div className="movie-description">
<span>{object.overview}</span>
</div>
</div>
</div>
))}
// Hiding the modal is just simply setting the selectedMovie to null
<Modal selectedMovie={selectedMovie} hideModal={() => setSelectedMovie(null)} />
<Pagination
dataPerPage={dataPerPage}
totalData={data && data.results.length}
paginate={paginate}
/>
</>
);
};

Unable to set property of a single element in an array

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>
);

Categories

Resources