I have a very simple https server I am talking to. It provides me with two calls (an authenticated GET and a POST for server-side storage of data). I am using two React functional components in my page. I load react.development.js so I can use React:
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
and at the end of my script I load the React functional components:
// This one is so we get it from the 'server' the first time:
// But it also prevents rendering, sadly
//localStorage.removeItem( "reminders");
const reminderRoot = ReactDOM.createRoot(document.getElementById('reminderset'));
reminderRoot.render(<ReminderSet />);
const loaderRoot = ReactDOM.createRoot(document.getElementById('jsoneditor'));
loaderRoot.render(<JSONEditor />);
The commented call removes the data from localStorage on initial load of the page. This forces a fetch from the server. The rest of the time, if the data changes, it is POST-ed to the server and the localStorage item is updated.
One component starts with:
const ReminderSet = () => {
const [reminders, setReminders] = useState(getMyReminders());
if (reminders === null || reminders === undefined || reminders === "" || reminders === "null") {
return (
<div className="ReminderSet">
<p>Loading...</p>
</div>
);
}
As long as the fetch() result has not been handled, the component shows “Loading…”
And the function providing the reminders data element is like this:
const getMyReminders = () => {
let storedReminders = localStorage.getItem( "reminders");
if (storedReminders === null || storedReminders === "") { // Check for empty string or null
fillReminderStorageFromServer();
}
return JSON.parse(storedReminders);
When localStorage is still empty, the function fillReminderStorageFromServer() tries to get them. This uses fetch() (because it must authenticate) which is asynchronous, so I have to handle the return. This function looks like this:
const fillReminderStorageFromServer = () => {
const myHeaders = new Headers();
const myToken = getToken();
let jsonResponse;
myHeaders.append( "Accept", "application/json");
myHeaders.append( "Content-type", "application/json; charset=UTF-8");
myHeaders.append( "Authorization", myToken);
const myURL = window.location.protocol + "//" + window.location.host + "/checkdo-get";
console.log( "GET: " + myURL);
const fetchPromise = fetch(myURL, {
method: 'GET',
mode: 'same-origin',
redirect: 'error',
cache: 'no-cache',
headers: myHeaders,
});
fetchPromise.then((rawResponse) => {
if (rawResponse.ok) {
const jsonPromise = rawResponse.json();
jsonPromise.then((jsonResponse) => {
localStorage.setItem( "reminders", JSON.stringify(jsonResponse));
// How do I get the component(s) to re-render?
// ReminderSet function component has: const [reminders, setReminders] = useState(getMyReminders());
// global script has: reminderRoot.render(<ReminderSet />);
window.dispatchEvent(window.alertEvent);
});
}
});
}
My problem is, when this function finally has the data, it must update localStorage (which it does) and it must make the React components to re-render (which it doesn’t). I don’t know how to use setReminders from outside the React component. I’ve tried to solve this by using a window.alertEvent which is handled inside the React component:
const forceUpdate = () => {
console.log("FORCEUPDATE!");
}
window.addEventListener('forceUpdate',forceUpdate);
with
window.alertEvent = new Event('forceUpdate');
at the start of my script. But the ‘FORCEUPDATE!’ log message is never seen.
As there are two React components on the page that use the same data, the data handling is outside both. But when the data arrives, I want to trigger an update of both components, which will mean I have to do something with setReminders or any other way. But how?
As it is now, it works when I uncomment the localStorage.removeItem( "reminders"); call. But the disadvantage of that is that when the server is used from different browsers, they will use (a possibly outdated) localStorage. So, if I can remove the localStorage cache when the page loads, but still get the component rendered correctly when the result of fetch() arrives, I have my working solution.