Reputation: 829
I am creating a page to update product details on an e-commerce site I am building using NextJS, and I have the image upload section nested inside an accordion on the individual item page. Once images have been uploaded, I would like to clear the upload form and close the accordion. It is closing the accordion I am having trouble with.
ImageUploadAccordion.js:
import React, {useRef} from 'react';
import {Accordion} from 'react-bootstrap'
import ImageUpload from './ImageUpload'
export default function ImageUploadAccordion({ item }) {
const accordionRef = useRef(null);
const toggleAccordion = () => {
accordionRef.current.click();
}
return (
<Accordion ref={accordionRef} defaultActiveKey="0">
<Accordion.Item eventKey="1">
<Accordion.Header>
<span className="btn btn-outline-success">Upload Images</span>
</Accordion.Header>
<Accordion.Body>
<ImageUpload
toggle={toggleAccordion}
item={item}
/>
</Accordion.Body>
</Accordion.Item>
</Accordion>
)
}
ImageUpload.js:
import React, {useState} from 'react';
import { useRouter } from 'next/router'
export default function ImageUpload({ item, toggle }) {
const router = useRouter()
const [images, setImages] = useState([])
const [imageURLS, setImageURLS] = useState([])
const [tags, setTags] = useState([])
const [theInputKey, setTheInputKey] = useState('')
const uploadImageToClient = (event) => {
if (event.target.files && event.target.files[0]) {
setImages((imageList) => [...imageList, {"index": images.length, "data": event.target.files[0]}]);
setImageURLS((urlList) => [
...urlList,
URL.createObjectURL(event.target.files[0])
]);
}
let randomString = Math.random().toString(36);
setTheInputKey(randomString)
};
const uploadTagToClient = (e) => {
if (event.target.value) {
const name = e.target.getAttribute("name")
// const i = event.target.value;
// document.getElementById("image-upload")
setTags((tagList) => [...tagList, {"name": name, "tag": e.target.value}]);
}
};
const removeImage = (name) => {
// debug
alert(`Trying to remove image index ${name}`)
let newImages = []
let newTags = []
setImages(images.filter(image => image.data.name !== name));
setTags(tags.filter(tag => tag.name !== name));
}
const uploadToServer = async (e) => {
const body = new FormData()
images.map((file, index) => {
body.append(`file${index}`, file.data);
});
// Use the filenames as keys then we can retrieve server side once we have the images
tags.map((tag, index) => {
body.append(tag.name, tag.tag)
})
const response = await fetch("/api/file", {
method: "POST",
"Content-Type": "multipart/form-data",
body
})
var message = await response.json();
alert(message['message'])
setImages([])
setTags([])
toggle()
};
const openImageUploadDialogue = () =>{
document.getElementById("image-upload").click()
}
return (
<div className="container">
<input style={{display:'none'}} accept="image/*" id="image-upload" type="file" key={theInputKey || '' } className="btn btn-outline-success-inverse" onChange={uploadImageToClient} />
<button className="btn btn-outline-success-inverse" onClick={openImageUploadDialogue} >
Add Image
</button>
<hr className = "text-pink"/>
<div className="row">
<div className="col d-flex flex-wrap">
{images.map((file, index) => {
return (
<div className="div p-1" key={file.data.name}>
<p className="text-pink">{file.data.name}</p>
<p>Tag</p>
<input type="text" name={file.data.name} id={`${file.data.name}`} onChange={uploadTagToClient} />
<img src={imageURLS[index]} height="200" width="150" />
<div className="btn btn-outline-success-inverse" onClick={ () =>removeImage(file.data.name)}>Remove Image</div>
</div>
);
})}
</div>
<button
className="btn btn-outline-success-inverse"
type="submit"
onClick={uploadToServer}
>
Upload Images
</button>
</div>
</div>
);
}
I tried by creating a reference to the accordion using useRef, and a function which uses this reference to activate the click event, which I passed to the ImageUpload component, according to another answer to a similar question, but it doesn't seem to work and I'm unsure as to why.
Any help always appreciated :-)
Upvotes: 2
Views: 1403
Reputation: 18939
I believe you have the wrong target as the ref
, update it to target the button that is automatically generated to wrap the header content.
<h2 class="accordion-header"><button type="button" aria-expanded="true" class="accordion-button"><span class="btn btn-outline-success">Upload Images</span></button></h2>
Rudimentary example:
export default function ImageUploadAccordion({ item }) {
const accordionRef = useRef(null);
const toggleAccordion = () => {
accordionRef.current.querySelector('button').click();
}
return (
<Accordion defaultActiveKey="0">
<Accordion.Item eventKey="1">
<Accordion.Header ref={accordionRef}>
<span className="btn btn-outline-success">Upload Images</span>
</Accordion.Header>
<Accordion.Body>
<ImageUpload
toggle={toggleAccordion}
item={item}
/>
</Accordion.Body>
</Accordion.Item>
</Accordion>
)
}
Upvotes: 1