Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions graphite-demo/frontend.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useEffect, useState } from 'react';

const TaskSearch = () => {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [searchQuery, setSearchQuery] = useState('');

useEffect(() => {
setLoading(true);
fetch(`/search?query=${encodeURIComponent(searchQuery)}`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
setTasks(data);
setLoading(false);
})
.catch(error => {
setError(error.message);
setLoading(false);
});
Comment on lines +22 to +25
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error state persists across requests. If a request fails and sets an error, then the user types again triggering a new successful request, the old error remains set. After the successful request completes (loading=false), line 32-34 will still display the error message instead of the results.

Fix by clearing error state when starting a new request:

useEffect(() => {
  setLoading(true);
  setError(null); // Clear previous error
  fetch(`/search?query=${encodeURIComponent(searchQuery)}`)
  // ... rest of the code
}, [searchQuery]);

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

}, [searchQuery]); // Depend on searchQuery
Comment on lines +9 to +26
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Race condition: Multiple fetch requests can be in flight simultaneously as the user types, and responses may arrive out of order. If a user types "cat" then quickly types "s" to make "cats", the response for "cat" might arrive after "cats", displaying wrong results.

Fix by implementing cleanup in useEffect:

useEffect(() => {
  const abortController = new AbortController();
  setLoading(true);
  fetch(`/search?query=${encodeURIComponent(searchQuery)}`, {
    signal: abortController.signal
  })
    .then(response => {
      if (!response.ok) throw new Error('Network response was not ok');
      return response.json();
    })
    .then(data => {
      setTasks(data);
      setLoading(false);
    })
    .catch(error => {
      if (error.name !== 'AbortError') {
        setError(error.message);
        setLoading(false);
      }
    });
  return () => abortController.abort();
}, [searchQuery]);
Suggested change
useEffect(() => {
setLoading(true);
fetch(`/search?query=${encodeURIComponent(searchQuery)}`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
setTasks(data);
setLoading(false);
})
.catch(error => {
setError(error.message);
setLoading(false);
});
}, [searchQuery]); // Depend on searchQuery
useEffect(() => {
const abortController = new AbortController();
setLoading(true);
fetch(`/search?query=${encodeURIComponent(searchQuery)}`, {
signal: abortController.signal
})
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then(data => {
setTasks(data);
setLoading(false);
})
.catch(error => {
if (error.name !== 'AbortError') {
setError(error.message);
setLoading(false);
}
});
return () => abortController.abort();
}, [searchQuery]); // Depend on searchQuery

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +9 to +26
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance issue: Every single keystroke triggers an immediate API request. If a user types "javascript" (10 characters), this generates 10 separate API calls. In production, this will cause excessive server load, potential rate limiting, and poor performance.

Fix by implementing debouncing:

useEffect(() => {
  const timeoutId = setTimeout(() => {
    setLoading(true);
    fetch(`/search?query=${encodeURIComponent(searchQuery)}`)
      .then(response => {
        if (!response.ok) throw new Error('Network response was not ok');
        return response.json();
      })
      .then(data => {
        setTasks(data);
        setLoading(false);
      })
      .catch(error => {
        setError(error.message);
        setLoading(false);
      });
  }, 300); // 300ms debounce
  
  return () => clearTimeout(timeoutId);
}, [searchQuery]);
Suggested change
useEffect(() => {
setLoading(true);
fetch(`/search?query=${encodeURIComponent(searchQuery)}`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
setTasks(data);
setLoading(false);
})
.catch(error => {
setError(error.message);
setLoading(false);
});
}, [searchQuery]); // Depend on searchQuery
useEffect(() => {
const timeoutId = setTimeout(() => {
setLoading(true);
fetch(`/search?query=${encodeURIComponent(searchQuery)}`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
setTasks(data);
setLoading(false);
})
.catch(error => {
setError(error.message);
setLoading(false);
});
}, 300); // 300ms debounce
return () => clearTimeout(timeoutId);
}, [searchQuery]); // Depend on searchQuery

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.


if (loading) {
return <div>Loading...</div>;
}

if (error) {
return <div>Error: {error}</div>;
}

return (
<div>
<h2>Task Search</h2>
<input
type="text"
placeholder="Search tasks..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
<ul>
{tasks.map(task => (
<li key={task.id}>
<p>{task.description}</p>
</li>
))}
</ul>
</div>
);
};

export default TaskSearch;
Loading