I've been trying for a while now to force useState
setter to behave in synchronous manner or to await for a state change before re-rendering. My simplified code:
import { useSelector } from "react-redux";
const SelectedFileContent = () => {
const selectedFile = useSelector(state => state.fileSelected);
const [fileContent, setFileContent] = useState(null);
const loadFileContent = async (name) => {
const f = await fetch(`static/${name}`).then(data => data.json());
console.log('In loadFileContent: ', fileContent)
return f;
}
const fetchFileContent = async () => {
await loadFileContent(selectedFile).then(data => setFileContent(data))
}
useEffect(() => {
fetchFileContent();
console.log('In useEffect: ', fileContent)
}, [selectedFile])
return (
<div>Selected file content: {fileContent}</div>
)
}
I've read multiple SO questions and answers but none of that worked for me. What my code does: first, it reads selectedFile
name from redux state. When this value changes, useEffect hook is being called (because of selectedFile
in dependency array). In useEffect, loadFileContent
function is being called, which in turn, loads file content and stores it in the fileContent
variable.
My issue is that since useState
setter is asynchronous and cannot be awaited, new value is being set too late and I can see with console logs in browser console that it's one state behind all the time - if for example, file 1.json
with content {"filename": "1.json"}
is being loaded, and then file 2.json
- those console logs are printing {"filename": "1.json"}
instead of {"filename": "2.json"}
, as I'd like to. What I would like to achieve is that on any re-render upon selectedFile
changes, correct file content will be stored in fileContent
const, without causing re-render when finished (This is just my assumption it happens though, not sure yet how to count it). But when I've added {console.log(fileContent)}
in the return statement, it logged file content correctly.
I've tried following things:
- in
fetchFileContent
, instead of passing loaded data directly to thesetFileContent
, split it in two lines - added loading/setLoading helper state -
setLoading(true)
beforefetchFileContent
and thensetLoading(false)
plus conditional rendering in the return statement ({fileContent && !loading ? ... : ...}
)
Also, when I've added additional useEffect
hook, it logged file content properly:
useEffect(() => {
console.log(fileContent);
}, [fileContent])