refactor(agent-streaming): Mount React app#119
Conversation
agduan
commented
Jun 12, 2026
- refactor: Add React entry point
- refactor: Update index.html with React root
- chore: Delete unused script.ts
There was a problem hiding this comment.
Code Review
This pull request migrates the client-side chat interface from static HTML to a React-based application, introducing a main App component to manage chat state and stream responses from the backend. The review feedback highlights critical improvements for the streaming implementation, including adding an AbortController to prevent memory leaks on unmounted components, allowing request cancellation, and changing the scroll behavior from 'smooth' to 'auto' to avoid performance degradation during rapid message updates.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
7db4252 to
612bacd
Compare
5a0537b to
542b08b
Compare
612bacd to
3fa3234
Compare
542b08b to
2c46183
Compare
|
Very clean, looks good to me, one thing that I might recommend is removing the script files since they're no longer being called. LGTM. |
3fa3234 to
afc01b3
Compare
2c46183 to
673ceeb
Compare
afc01b3 to
ced3cbd
Compare
673ceeb to
77c4cc3
Compare
ced3cbd to
7c8b888
Compare
jhuleatt
left a comment
There was a problem hiding this comment.
Nice! I left some ideas to help simplify this
7c8b888 to
c91b8ad
Compare
77c4cc3 to
5d39572
Compare
| const chunk = JSON.parse(jsonStr) as StreamChunk; | ||
| handleStreamChunk(chunk); | ||
| } catch (err) { | ||
| console.error('Failed to parse SSE JSON:', err); |
There was a problem hiding this comment.
Will swallowing this error lead to weirdly formatted messages (a message missing a data chunk in the middle)? Should this throw instead?
There was a problem hiding this comment.
Yes, good idea! I resolved by removing the inner try/catch block entirely so parsing errors throw immediately & trigger the outer catch block.
| if (trimmed.startsWith('data: ')) { | ||
| const jsonStr = trimmed.slice(6); |
There was a problem hiding this comment.
suggestion to make this less brittle:
| if (trimmed.startsWith('data: ')) { | |
| const jsonStr = trimmed.slice(6); | |
| const dataPrefix = 'data: '; | |
| if (trimmed.startsWith(dataPrefix)) { | |
| const jsonStr = trimmed.slice(dataPrefix.length); |
There was a problem hiding this comment.
For my own learning, is this data: prefix a Genkit thing? Or another standard?
There was a problem hiding this comment.
Done! data is one of the standard field names in HTML5's SSE protocol (which enforces the field: value format for events).
| const { value, done } = await reader.read(); | ||
| if (done) break; |
There was a problem hiding this comment.
do you need to check controller.signal.aborted here before the next read() (and break if aborted is true), or does reader.read() do that automatically?
| buffer += decoder.decode(value, { stream: true }); | ||
| const lines = buffer.split('\n'); | ||
|
|
||
| // Keep the last partial line in the buffer |
There was a problem hiding this comment.
Can you add a "why" to the end of this comment? If I'm understanding this, maybe:
| // Keep the last partial line in the buffer | |
| // Keep only the last partial line in the buffer to concat with the next chunk |
There was a problem hiding this comment.
Done with // Store the last partial line in the buffer to prevent parsing errors if a chunk is split mid-line
|
|
||
| for (const line of lines) { | ||
| const trimmed = line.trim(); | ||
| if (trimmed.startsWith('data: ')) { |
There was a problem hiding this comment.
What should happen if a line doesn't start with data: ? It looks like it is just ignored at the moment
There was a problem hiding this comment.
The server is streaming response chunks using the SSE format (data: <JSON stuff>\n\n). So when the buffer is split on newlines, we either get valid data starting with data: that we should parse, or empty lines that we can just ignore
I think edge cases should already be handled in the rest of the program - invalid JSON gets caught and server errors are still prefixed with data
| if (trimmed.startsWith('data: ')) { | ||
| const jsonStr = trimmed.slice(6); | ||
| try { | ||
| const chunk = JSON.parse(jsonStr) as StreamChunk; |
There was a problem hiding this comment.
you could do some checking to make sure it actually is a StreamChunk (or that could be overkill for a sample)
There was a problem hiding this comment.
Probably going to go with overkill since we already have validation on server side, but def will consider as best practice for future apps
- refactor: Add React entry point - refactor: Update index.html with React root - chore: Delete unused script.ts
c91b8ad to
9c059ae
Compare
5d39572 to
35721ec
Compare
32b04ee
into
agent-streaming/4-react-components