Removing FilePond file after upload React - reactjs

Ok I am at my wits end here. I am using FilePond with React in a functional component using Hooks. I am not using FilePond to handle the actual upload, just setting the state with the files, my simplified version is this:
The Filepond:
<FilePond
onupdatefiles={fileItems => handleFilepondUpdate(fileItems)}
/>
</Form.Field>
The handle update:
const handleFilepondUpdate = fileItems => {
if (fileItems.length === 0) {
addAttachment({
...picture,
bugAttachment: null
});
} else {
addAttachment({
...picture,
bugAttachment: fileItems[0].file
});
}
};
The state:
const [picture, addAttachment] = useState({
bugAttachment: ""
});
const { bugAttachment } = picture;
And finally my upload and clear the input state:
const onSubmit = e => {
e.preventDefault();
const fd = new FormData();
fd.append("email", props.user[0].email);
fd.append("bugDescription", bugDescription);
fd.append("bugAttachment", bugAttachment);
addBug(fd).then(() => {
setBug({
bugDescription: ""
});
});
};
So how would I go about removing the FilePond file after the form is sent through?

Try clearing the bugAttachment property inside onSubmit using addAttachment hook
const onSubmit = e => {
e.preventDefault();
const fd = new FormData();
fd.append("email", props.user[0].email);
fd.append("bugDescription", bugDescription);
fd.append("bugAttachment", bugAttachment);
addBug(fd).then(() => {
setBug({
bugDescription: ""
});
addAttachment({
...picture,
bugAttachment:""
});
});
};
Update:
It seems like that you have not used the files prop with your picture state,Try something like this.
<FilePond
files={bugAttachment}
onupdatefiles={fileItems => handleFilepondUpdate(fileItems)}
/>

Related

How to use PDF.JS with React?

I would like to parse a pdf file in a React app. The pdf will be provided through a html input.
I used pdf-parse - a wrapper around pdf.js in node - without any problem. But when it comes to React, I only receive this error:
MissingPDFException {message: 'Missing PDF "http://localhost:3000/myfile.pdf".', name: 'MissingPDFException'}
I upload the file like this:
export default function Home() {
const [data, setData] = useState();
const handleFile = (e) => {
const file = e.target.files[0];
const fileReader = new FileReader();
fileReader.onload = (d) => {
setData(new Uint32Array(d.target.result));
};
};
return (
<>
<h1>hello!</h1>
<input
type="file"
accept="application/pdf"
placeholder="insert PDF here"
onChange={(e) => handleFile(e)}
/>
<PDFViewer pdfFile={data} />
</>
);
}
And The file is supposed to be read here:
import * as PDFJS from "pdfjs-dist/build/pdf";
import * as pdfjsWorker from "pdfjs-dist/build/pdf.worker.entry";
window.PDFJS = PDFJS;
export default function PDFViewer({ pdfFile }) {
PDFJS.GlobalWorkerOptions.workerSrc = pdfjsWorker;
const getPDFDoc = useCallback(async () => {
const doc = await PDFJS.getDocument(pdfFile);
doc.promise.then(
(loadedPdf) => {
setPdfRef(loadedPdf);
},
function (reason) {
console.error(reason);
}
);
}, []);
useEffect(() => {
getPDFDoc();
}, [getPDFDoc]);
I doesn't seem to work at all. I have a custom config with webpack, typescript and SWC-loader. I have read all the related stackoverflow threads.
How to properly parse a PDF with PDF.js in React? If there is a better library, I'm open to any suggestions. My goal is not to display the pdf, but to get its content.
Your component only runs getPDFDoc on mount since pdfFile is missing in the usecallback deps, so when the file changes, it probably doesn't even notice as your effect won't re-run since getPDFDoc is referentially stable when it shouldn't be.
Try
import * as PDFJS from "pdfjs-dist/build/pdf";
import * as pdfjsWorker from "pdfjs-dist/build/pdf.worker.entry";
window.PDFJS = PDFJS;
export default function PDFViewer({ pdfFile }) {
PDFJS.GlobalWorkerOptions.workerSrc = pdfjsWorker;
const getPDFDoc = useCallback(async () => {
if (!pdfFile) return
const doc = await PDFJS.getDocument(pdfFile);
doc.promise.then(
(loadedPdf) => {
setPdfRef(loadedPdf);
},
function (reason) {
console.error(reason);
}
);
}, [pdfFile]);
useEffect(() => {
getPDFDoc();
}, [getPDFDoc]);
I think the reason for the weird "myfile.pdf" thing is probably because when it first runs pdfFile is not defined and this might be some internal library default. So I also added a guard to not do anything when it's not set.

Ant design on preview is not getting fired

I have an upload button like this. I want to show preview of uploaded Image on a div. Hence, I want the previewImage. This is how i'm trying to achieve it but onPreview is not getting fired at all.
It's a functional component. Sandbox=> https://codesandbox.io/s/silly-breeze-2gvewe
function AddAttachment(props) {
const getBase64 = (file)=>{
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
const onChange = ({ fileList: newFileList }) => {
setFileList(()=>(newFileList));
console.log(fileList);
};
const handlePreviewImage = async (file)=>{
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
setBase64({
previewImage: file.url || file.preview,
previewVisible: true,
previewTitle: file.name || file.url.substring(file.url.lastIndexOf('/') + 1),
});
}
return (
<UploadButton
fileList={fileList}
onPreview ={handlePreviewImage}
onChange={onChange}
aspect={2} listType="picture" />
)
}
From the docs:
onPreview
A callback function, will be executed when file link or preview icon is clicked.
When you try clicking on the preview image or link, you should see handlePreviewImage get called.

React hooks : how to watch changes in a JS class object?

I'm quite new to React and I don't always understand when I have to use hooks and when I don't need them.
What I understand is that you can get/set a state by using
const [myState, setMyState] = React.useState(myStateValue);
So. My component runs some functions based on the url prop :
const playlist = new PlaylistObj();
React.useEffect(() => {
playlist.loadUrl(props.url).then(function(){
console.log("LOADED!");
})
}, [props.url]);
Inside my PlaylistObj class, I have an async function loadUrl(url) that
sets the apiLoading property of the playlist to true
gets content
sets the apiLoading property of the playlist to false
Now, I want to use that value in my React component, so I can set its classes (i'm using classnames) :
<div
className={classNames({
'api-loading': playlist.apiLoading
})}
>
But it doesn't work; the class is not updated, even if i DO get the "LOADED!" message in the console.
It seems that the playlist object is not "watched" by React. Maybe I should use react state here, but how ?
I tested
const [playlist, setPlaylist] = React.useState(new PlaylistObj());
React.useEffect(() => {
//refresh playlist if its URL is updated
playlist.loadUrl(props.playlistUrl).then(function(){
console.log("LOADED!");
})
}, [props.playlistUrl]);
And this, but it seems more and more unlogical to me, and, well, does not work.
const [playlist, setPlaylist] = React.useState(new PlaylistObj());
React.useEffect(() => {
playlist.loadUrl(props.playlistUrl).then(function(){
console.log("LOADED!");
setPlaylist(playlist); //added this
})
}, [props.playlistUrl]);
I just want my component be up-to-date with the playlist object. How should I handle this ?
I feel like I'm missing something.
Thanks a lot!
I think you are close, but basically this issue is you are not actually updating a state reference to trigger another rerender with the correct loading value.
const [playlist, setPlaylist] = React.useState(new PlaylistObj());
React.useEffect(() => {
playlist.loadUrl(props.playlistUrl).then(function(){
setPlaylist(playlist); // <-- this playlist reference doesn't change
})
}, [props.playlistUrl]);
I think you should introduce a second isLoading state to your component. When the effect is triggered whtn the URL updates, start by setting loading true, and when the Promise resolves update it back to false.
const [playlist] = React.useState(new PlaylistObj());
const [isloading, setIsLoading] = React.useState(false);
React.useEffect(() => {
setIsLoading(true);
playlist.loadUrl(props.playlistUrl).then(function(){
console.log("LOADED!");
setIsLoading(false);
});
}, [props.playlistUrl]);
Use the isLoading state in the render
<div
className={classNames({
'api-loading': isLoading,
})}
>
I also suggest using the finally block of a Promise chain to end the loading in the case that the Promise is rejected your UI doesn't get stuck in the loading "state".
React.useEffect(() => {
setIsLoading(true);
playlist.loadUrl(props.playlistUrl)
.then(function() {
console.log("LOADED!");
})
.finally(() => setIsLoading(false));
}, [props.playlistUrl]);
Here you go:
import React from "react";
class PlaylistAPI {
constructor(data = []) {
this.data = data;
this.listeners = [];
}
addListener(fn) {
this.listeners.push(fn);
}
removeEventListener(fn) {
this.listeners = this.listeners.filter(prevFn => prevFn !== fn)
}
setPlayList(data) {
this.data = data;
this.notif();
}
loadUrl(url) {
console.log("called loadUrl", url, this.data)
}
notif() {
this.listeners.forEach(fn => fn());
}
}
export default function App() {
const API = React.useMemo(() => new PlaylistAPI(), []);
React.useEffect(() => {
API.addListener(loadPlaylist);
/**
* Update your playlist and when user job has done, listerners will be called
*/
setTimeout(() => {
API.setPlayList([1,2,3])
}, 3000)
return () => {
API.removeEventListener(loadPlaylist);
}
}, [API])
function loadPlaylist() {
API.loadUrl("my url");
}
return (
<div className="App">
<h1>Watching an object by React Hooks</h1>
</div>
);
}
Demo in Codesandbox

InvalidStateError: Failed to execute 'stop' on 'MediaRecorder': The MediaRecorder's state is 'inactive'

const [rec, setRec] = useState({});
const [onRec, setOnRec] = useState(true);
useEffect(() => {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
const mediaRecorder = new MediaRecorder(stream)
setRec(mediaRecorder)
})
}, [onRec]);
This is useEffect.
const onRecAudio = () => {
rec.start()
console.log(rec);
console.log("start")
setOnRec(false)
}
This is first click of function. recording start.
const offRecAudio = () => {
rec.stop()
console.log("stop")
setOnRec(true)
}
This is second click of function. recording end.
<button onClick={onRec ? onRecAudio : offRecAudio } />
I don't want the useEffect to run those statements when the component is first rendered, but just click a button to run them. Press once to start recording, press again to end recording. But when I press it again, I see this error.
I've come across the same error but with different patter, I didn't save MediaRecorder in state because I found it's hard to deal with complex objects and Web API interfaces when saving them and restore them from the state, so I used a react ref and two buttons for start and stop, the error was showing when I've forget to unbind the stop event listener, but after unbinding it, it works great.
I'll share my code with you and for the future people who want to record audio using react in a simple way:
import { useRef } from 'react';
declare var MediaRecorder: any;
export function ChatsInputs() {
const stopButtonRef = useRef<HTMLButtonElement>(null);
function startRecording() {
navigator
.mediaDevices
.getUserMedia({ audio: true, video: false })
.then(function (stream) {
const options = { mimeType: 'audio/webm' };
const recordedChunks: any = [];
const mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.addEventListener('dataavailable', function (e: any) {
if (e.data.size > 0) recordedChunks.push(e.data);
});
mediaRecorder.addEventListener('stop', function () {
setBlobUrl(URL.createObjectURL(new Blob(recordedChunks)));
});
if (stopButtonRef && stopButtonRef.current)
stopButtonRef?.current?.addEventListener('click', function onStopClick() {
mediaRecorder.stop();
this.removeEventListener('click', onStopClick)
});
mediaRecorder.start();
});
}
return (
<div>
<button onClick={startRecording}>{'rec'}</button>
<button ref={stopButtonRef}>{'stop'}</button>
<a download="file.wav" href={blobUrl}>{'download audio'}</a>
{
blobUrl ?
<audio id="player" src={blobUrl} controls></audio>
:
null
}
</div>
);
}
Ref Article

Update CkEditor config on the basis of React state

I am using CkEditor5 for implementing CKEditor in my React application. I want to update the config wrt my state. But it seems like the editor component isn't updating even after the state change.
Here is my CKEditor component
<CKEditor
editor={ ClassicEditor }
data={this.state.content}
onInit={ editor => {
console.log( 'Editor is ready to use!', editor );
} }
config={{
simpleUpload: {
uploadUrl: uploadUrl,
headers: {
'X-CSRFToken': csrftoken,
}
}}}
onChange={ ( event, editor ) => {
const data = editor.getData();
this.setState({
content: data
})
console.log( { event, editor, data } );
} }
onBlur={ ( event, editor ) => {
console.log( 'Blur.', editor );
} }
onFocus={ ( event, editor ) => {
console.log( 'Focus.', editor );
} }
/>
I want to update the upload URL based on the state.
It is stored in a variable like shown below -
const csrftoken = getCookie('csrftoken');
const uploadUrl = `https://example.com/api/blog/posts/images/${this.state.id}`
I solved this by using ClassicEditor instead of the CKEditor component.
I did this by writing two function for this. First getEditorConfig which would give me the changed config based on other parameters.
getEditorConfig = () => {
const csrftoken = getCookie('csrftoken');
const debug = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1"
const uploadUrl = debug ? `http://127.0.0.1:8000/api/blog/posts/images/${this.state.id}`
: `https://example.com/api/blog/posts/images/${this.state.id}`
return {
simpleUpload: {
uploadUrl: uploadUrl,
headers: {
'X-CSRFToken': csrftoken,
}
}}
}
Next a helper function which creates a new editor instance and adds it to the DOM. I am storing the editor instance in the state.
addEditor = () => {
const config = this.getEditorConfig();
ClassicEditor
.create( document.querySelector( '#editor' ), config)
.then(newEditor => this.setState({editor: newEditor}))
.catch( error => {
console.log( error );
});
}
In one of the issues its suggested to destroy the old editor and create a new one when you want to update the config. So for that use the following two lines to destroy and use addEditor to create a new editor.
const { editor } = this.state;
editor.destroy();
My solution consists in adding ref and custom states to the editor e using a custom upload adapter. Now, I'm able to send the current state data to server and without using headers to that.
<CKEditor name="content" ref={editorRef}
editor={Editor}
config={customEditorConfig}
data={article.content}
customPropArticle={article}
onReady={editor => {
editor.editorRef = editorRef;
}}
onChange={onEditorChangeHandle}
/>
From UploadAdapter:
_sendRequest() {
const editorRef = this.editor.editorRef;
const { customPropArticle } = editorRef.current.props;
this.loader.file.then(file => {
const data = new FormData();
data.append('id', customPropArticle.Id);
data.append('upload', file);
this.xhr.send(data);
});

Resources