fix(api): ensure files array is never null in API response
Add null safety checks to prevent TypeError when backend returns null instead of empty array for files list. Initialize empty slices on backend and add null coalescing on frontend when accessing files state. - Backend: Initialize files slice to always return [] instead of null - Frontend: Add null checks for files state in all map/filter operations
This commit is contained in:
@@ -52,6 +52,11 @@ func (h *Handlers) ListFiles(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure we always encode an array, never null
|
||||
if files == nil {
|
||||
files = []*storage.File{}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(files)
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func (s *Storage) List() ([]*File, error) {
|
||||
return nil, fmt.Errorf("failed to read directory: %w", err)
|
||||
}
|
||||
|
||||
var files []*File
|
||||
files := make([]*File, 0)
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".md") {
|
||||
continue
|
||||
|
||||
@@ -20,7 +20,7 @@ function App() {
|
||||
try {
|
||||
setError(null);
|
||||
const fetchedFiles = await api.list();
|
||||
setFiles(fetchedFiles);
|
||||
setFiles(fetchedFiles ?? []);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to load files');
|
||||
} finally {
|
||||
@@ -56,7 +56,7 @@ function App() {
|
||||
try {
|
||||
setError(null);
|
||||
const file = await api.create(name, '# ' + name.replace('.md', '') + '\n\nStart writing here...');
|
||||
setFiles([...files, file]);
|
||||
setFiles([...(files ?? []), file]);
|
||||
setSelectedFile(file);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to create file');
|
||||
@@ -72,7 +72,7 @@ function App() {
|
||||
const updated = await api.update(selectedFile.name, content);
|
||||
setSelectedFile(updated);
|
||||
setOriginalContent(content);
|
||||
setFiles(files.map((f) => (f.name === updated.name ? updated : f)));
|
||||
setFiles((files ?? []).map((f) => (f.name === updated.name ? updated : f)));
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to save file');
|
||||
} finally {
|
||||
@@ -86,7 +86,7 @@ function App() {
|
||||
try {
|
||||
setError(null);
|
||||
await api.delete(name);
|
||||
setFiles(files.filter((f) => f.name !== name));
|
||||
setFiles((files ?? []).filter((f) => f.name !== name));
|
||||
if (selectedFile?.name === name) {
|
||||
setSelectedFile(null);
|
||||
}
|
||||
@@ -125,7 +125,7 @@ function App() {
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4">
|
||||
<div className="lg:col-span-1">
|
||||
<FileList
|
||||
files={files}
|
||||
files={files ?? []}
|
||||
selectedFile={selectedFile?.name || null}
|
||||
onSelectFile={handleSelectFile}
|
||||
onDeleteFile={handleDeleteFile}
|
||||
@@ -163,7 +163,7 @@ function App() {
|
||||
isOpen={showNewDialog}
|
||||
onClose={() => setShowNewDialog(false)}
|
||||
onCreate={handleCreateFile}
|
||||
existingNames={files.map((f) => f.name)}
|
||||
existingNames={files?.map((f) => f.name) ?? []}
|
||||
/>
|
||||
|
||||
{hasUnsavedChanges && (
|
||||
|
||||
Reference in New Issue
Block a user