I have a page called Dashboard
and on this page I have a component called Dropzone
which is used for uploading a file to the page.
When a file is uploaded, it calls the onDrop()
callback where I POST the file to my api, and I then try to sent a GET request to my api at /api/machines/{hash}
. I want to set my state.pcapAnalysing
variable to TRUE
before this GET request, and set it to FALSE
after it finishes. The idea of this is to show an 'analysing' message on the Dashboard page while the api call is running, this is likely to take a few seconds to return.
Due to the state not being refreshed, the message does not show as the state never updates to show state.pcapAnalysing
as anything but false. Does anyone have any idea how I can achieve the effect I'm looking for? Essentially, I'm trying to show a message on the Dashboard
page for the duration of the api call action, but have this triggered by the component residing in Dropzone.js
. Thanks.
Dashboard.js
...
export default function Dashboard() {
const [currentTime, setCurrentTime] = useState(0);
const [state, dispatch] = useContext(Context);
useEffect(() => {
fetch('/api/time').then(res => res.json()).then(data => {
setCurrentTime(data.time);
});
}, []);
return (
<PageBase navigation={navigation}>
<div className="flex place-content-center mt-10">
<div className="flex w-1/2 place-content-center">
{ !state.pcapUploaded ?
state.pcapAnalysing ?
<p>uploading</p> // Want to show this when state.pcapAnalysing is TRUE
:
<TwoCard numcols="2">
<div>
<h5 className="">Upload a PCAP file to analyse</h5>
<p>Accepted file types: .pcap, .pcapng, .tcpdump</p>
</div>
<div className="mt-5 lg:ml-2 lg:mt-0 md:mt-0">
<MyDropzone/>
</div>
</TwoCard>
:
<TwoCard numcols="3">
<div className='col-span-1'>
<img src={require("../Assets/pcap.png")}/>
</div>
<div className='col-span-2'>
<h5 className="">Selected File:</h5>
<p className="break-all">{state.pcapFileName}</p>
<p className="break-all">{state.pcapHash}</p>
<button type="button" onClick={clearPcap}>
Change
</button>
</div>
</TwoCard>
}
</div>
</div>
<div>
{ state.pcapUploaded ? <TileGrid tiles={tiles}/> : null }
</div>
</PageBase>
);
}
Dropzone.js
import { useCallback, useEffect, useContext } from 'react';
import { useDropzone } from 'react-dropzone';
import { Context } from '../Helpers/Provider';
export default function MyDropzone() {
const [state, dispatch] = useContext(Context);
const onDrop = useCallback(acceptedFiles => {
const formData = new FormData();
formData.append('file', acceptedFiles[0]);
fetch('/api/upload',
{
method: 'POST',
body: formData,
}
)
.then(res => res.json())
.then(result => {
console.log('Success: ', result);
console.log("dispatching...");
dispatch({
type: 'HASH',
payload: result['hash'],
});
dispatch({
type: 'ANALYSING', // Want to use this to set state.pcapAnalysing to TRUE
});
console.log("before: " state.pcapAnalysing);
if (state.machineIPs == null) {
console.log("Machines: ", state.pcapHash);
fetch(`/api/machines/${result['hash']}`) // This request may take a few seconds
.then(res => res.json())
.then(data => {
console.log(data);
dispatch({
type: 'MACHINES',
payload: result,
});
});
};
dispatch({
type: 'ANALYSING', // Want to use this to set state.pcapAnalysing to false after request completes
});
console.log("after: " state.pcapAnalysing);
})
}, [state.pcapHash])
const {getRootProps, getInputProps, isDragActive} = useDropzone({
onDrop,
maxFiles: 1,
accept: '.pcap, .pcapng, .tcpdump'
})
return (
<div {...getRootProps()} className="..<shortened>..">
Upload PCAP
<input {...getInputProps()} />
{
isDragActive ?
<p>Drop the file here ...</p> :
<p className="ml-2 mr-2">Drag 'n' drop a file here, or click to select file</p>
}
</div>
)
}
CodePudding user response:
Looks like you should be setting the final state within the then
close of your /api/machines
call. Not after it.
So the whole call should look like this:
fetch('/api/upload',
{
method: 'POST',
body: formData,
}
)
.then(res => res.json())
.then(result => {
console.log('Success: ', result);
console.log("dispatching...");
dispatch({
type: 'HASH',
payload: result['hash'],
});
dispatch({
type: 'ANALYSING', // Want to use this to set state.pcapAnalysing to TRUE
});
console.log("before: " state.pcapAnalysing);
if (state.machineIPs == null) {
console.log("Machines: ", state.pcapHash);
fetch(`/api/machines/${result['hash']}`) // This request may take a few seconds
.then(res => res.json())
.then(data => {
console.log(data);
dispatch({
type: 'ANALYSING', // Want to use this to set state.pcapAnalysing to false after request completes
});
dispatch({
type: 'MACHINES',
payload: result,
});
});
};
})
}, [state.pcapHash])
Notice I removed that line altogether
console.log("after: " state.pcapAnalysing);
Because even if I moved it within the then
close, the state wouldn't have been updated yet, because the component re-render hasn't occurred yet.