czyli jak to w praktyce powinno wyglądać? Chcę żeby user klikając "save" zapisał coś do pliku, a jak sobie przeładuje stronę albo kliknie "reload" to żeby aplikacja odczytała plik i wyświetliła informacje z niego w UI.
Powinieneś mieć klasę która umie operować plikiem i wystawia prosty interfejs, np. fs.saveFile('hello', onSave);
oraz fs.openFile(onOpen)
. Komponent może wtedy wywołać te metody i przekazać swoje funkcje jako parametr żeby być powiadomionym o wyniku. Dodatkowo chcesz być powiadomiony o błędzie, czyli ta funkcja powinna oprócz wartości zwrócić informację o tym czy był błąd czy nie, może to być true
/false
.
Instancja Twojej klasy może być przekazana do komponentu albo przez property albo przez context/provide (przy czym property są łatwiejsze przy małych projektach). Jej instancja może być stworzona zanim React wstanie, ewentualnie któryś z komponentów może ją stworzyć (tylko wtedy widok wie więcej o aplikacji, co nie koniecznie jest dobre, bo zmiana Twojej klasy wpłynie na zmianę komponentu).
Co powinno zarządzać takimi operacjami i jeśli jednak klasa typu Singleton na jakim etapie ją utworzyć?
Część powinna być po stronie widoku, część po stronie Twojej klasy:
Przykład kodu poniżej. Zauważ że klasa MyFilesystem
w ogóle nie musi używać systemu plików na potrzeby przykładu, implementację można łatwo zastąpić.
import React, {useState} from 'react';
import ReactDOM from 'react-dom/client';
class MyFilesystem {
saveFile(content, onSaveOrError) {
console.log('saving a file', content);
if (Math.random() < 0.5) { // simulate error
onSaveOrError(true); // success
} else {
onSaveOrError(false); // there was an error
}
}
openFile(onOpenOrError) {
console.log('reading a file');
if (Math.random() < 0.5) { // simulate error
onOpenOrError(true, 'content loaded from file'); // success
} else {
onOpenOrError(false, null); // there was an error
}
}
}
function MyComponent({ fs }) {
const [text, setText] = useState('');
const [errorMessage, setErrorMessage] = useState(null);
function handleSave() {
fs.saveFile(text, (success) => {
setErrorMessage(!success ? 'There was an error saving a file.' : 'Saved file.');
});
}
function handleOpen() {
fs.openFile((success, content) => {
if (success) {
setText(content );
setErrorMessage('Opened file.');
} else {
setText('');
setErrorMessage('There was an error opening a file.');
}
});
}
useEffect(() => handleOpen(), []); // invoke open on first render
return (
<div>
<button onClick={handleSave}>Save</button>
<button onClick={handleOpen}>Open</button>
<div>
<textarea value={text} onChange={e=>setText(e.target.value)}/>
</div>
<p>{errorMessage}</p>
</div>
);
}
const myFileSystem = new MyFilesystem();
ReactDOM.createRoot(document.getElementById('root')).render(
<MyComponent fs={myFileSystem} />
);