Skip to content

Conversation

@Henry8192
Copy link
Contributor

@Henry8192 Henry8192 commented Jun 25, 2025

Description

Fixes #299.

Checklist

Validation performed

Summary by CodeRabbit

  • Refactor

    • Enhanced performance and stability by optimizing state access and memoizing callbacks in multiple components, reducing unnecessary re-renders.
    • Streamlined file handling and notification processes for improved maintainability.
  • Chores

    • Removed redundant internal setter methods in file state management without affecting user-facing functionality.

@coderabbitai
Copy link

coderabbitai bot commented Jun 25, 2025

## Walkthrough

This change refactors multiple components to access Zustand store actions using `useStore.getState()` directly at invocation time, rather than subscribing to actions via hooks. Several setter methods are removed from the store interface, and store updates now use direct partial state updates. Memoization with `useCallback` is applied to relevant handlers.

## Changes

| File(s)                                                                                      | Change Summary                                                                                                    |
|---------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
| src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx, src/components/CentralContainer/Sidebar/index.tsx | Refactored to access store setters via `useUiStore.getState()` within callbacks; memoized handlers with `useCallback`.         |
| src/components/DropFileContainer/index.tsx, src/components/MenuBar/index.tsx                 | Store actions accessed via `useLogFileStore.getState()`; handlers wrapped with `useCallback` for memoization.     |
| src/components/MenuBar/ExportLogsButton.tsx                                                  | Store action accessed via `useLogExportStore.getState()` in a memoized callback.                                 |
| src/components/PopUps/PopUpMessageBox.tsx                                                   | Store action accessed via `useNotificationStore.getState()` in memoized and effect callbacks.                    |
| src/stores/logFileStore.ts                                                                  | Removed explicit setter methods from interface and implementation; store updates use direct partial state updates.|

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant UIComponent
    participant ZustandStore

    UIComponent->>ZustandStore: getState().action()
    Note right of ZustandStore: Action is invoked directly from the current state
    ZustandStore-->>UIComponent: Updates state (no subscription for action)

Assessment against linked issues

Objective (Issue #) Addressed Explanation
Use getState() for immutable Zustand actions (#299)

Possibly related PRs

Suggested reviewers

  • junhaoliao

<!-- walkthrough_end -->
<!-- This is an auto-generated comment: all tool run failures by coderabbit.ai -->

> [!WARNING]
> There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.
> 
> <details>
> <summary>🔧 ESLint</summary>
> 
> > If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.
> 
> npm error Exit handler never called!
> npm error This is an error with npm itself. Please report this error at:
> npm error   <https://github.com/npm/cli/issues>
> npm error A complete log of this run can be found in: /.npm/_logs/2025-06-26T22_38_38_862Z-debug-0.log
> 
> 
> 
> </details>

<!-- end of auto-generated comment: all tool run failures by coderabbit.ai -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 2ca88da4d0c8d077ac62f5fff27671bb141f526d and 137548b4c9761ef333092a1e5bc8e4e93347e0d9.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `src/stores/logFileStore.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == <expression>` rather than `!<expression>`.</summary>


> `**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == <expression>` rather than `!<expression>`.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:
- `src/stores/logFileStore.ts`

</details>

</details><details>
<summary>🧠 Learnings (2)</summary>

<details>
<summary>📓 Common learnings</summary>

Learnt from: zzxthehappiest
PR: #286
File: src/components/Editor/index.tsx:138-139
Timestamp: 2025-06-01T13:40:12.222Z
Learning: In the yscope-log-viewer codebase, when using Zustand stores in React components, the preferred pattern is to use getState() for static setters that never change (like setLogEventNum) to avoid unnecessary subscriptions, while using hooks for actions that do more than just setting values. All store state variables should be declared at the beginning of the component for consistency and clear dependency overview.


Learnt from: junhaoliao
PR: #265
File: src/stores/logFileStore.ts:125-125
Timestamp: 2025-05-17T05:13:39.947Z
Learning: When refactoring Zustand state imports to prevent circular dependencies in the yscope-log-viewer project, the preferred pattern is to move state retrievals closest to where they're actually used rather than retrieving them at the beginning of the function.


Learnt from: junhaoliao
PR: #94
File: new-log-viewer/src/components/CentralContainer/Sidebar/index.tsx:99-118
Timestamp: 2024-10-19T03:33:29.578Z
Learning: In new-log-viewer/src/components/CentralContainer/Sidebar/index.tsx, when using useEffect to register window resize event handlers, it's acceptable to have an empty dependency array because the variables and functions used within the effect (getPanelWidth, PANEL_CLIP_THRESHOLD_IN_PIXELS, and tabListRef) are constants or refs declared outside the functional component and do not change.


Learnt from: junhaoliao
PR: #84
File: new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx:99-114
Timestamp: 2024-10-08T15:52:50.753Z
Learning: When suggesting to add validation for form inputs in SettingsDialog.tsx, the user considered it not related to the PR. In future reviews, ensure suggestions align with the PR's scope.


Learnt from: junhaoliao
PR: #84
File: new-log-viewer/src/components/modals/SettingsModal/SettingsDialog.tsx:99-114
Timestamp: 2024-09-30T20:49:32.508Z
Learning: When suggesting to add validation for form inputs in SettingsDialog.tsx, the user considered it not related to the PR. In future reviews, ensure suggestions align with the PR's scope.


Learnt from: Henry8192
PR: #107
File: src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx:1-7
Timestamp: 2024-10-28T18:40:29.816Z
Learning: In the y-scope/yscope-log-viewer project, importing components via destructuring from '@mui/joy' is acceptable, as the package size impact has been analyzed and found acceptable.


Learnt from: junhaoliao
PR: #224
File: src/contexts/states/viewStore.ts:108-109
Timestamp: 2025-05-09T01:07:32.803Z
Learning: For the viewStore in the yscope-log-viewer project, it's more appropriate to suppress the max-lines-per-function ESLint rule than to split the store into slices due to the tight coupling and cohesive nature of its functions.


Learnt from: junhaoliao
PR: #286
File: src/stores/viewStore.ts:46-46
Timestamp: 2025-05-21T20:08:19.249Z
Learning: For Zustand store actions, use setX for functions that simply change the value of a state, and use updateX for functions that do more than just set a value (e.g., perform additional logic, make API calls, update multiple states).


Learnt from: junhaoliao
PR: #286
File: src/stores/viewStore.ts:46-46
Timestamp: 2025-05-21T20:08:19.249Z
Learning: For Zustand store actions, use setX for functions that simply change the value of a state, and use updateX for functions that do more than just set a value (e.g., perform additional logic, make API calls, update multiple states).


Learnt from: junhaoliao
PR: #224
File: src/contexts/states/logFileStore/loadSlice.ts:1-1
Timestamp: 2025-04-28T08:21:28.568Z
Learning: When importing from npm packages, use the public API entrypoint (e.g., import {StateCreator} from "zustand") rather than deep-linking into package internals (e.g., import {StateCreator} from "zustand/index"), as internal file structures may change between package versions.


Learnt from: junhaoliao
PR: #74
File: new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomListItem.tsx:31-31
Timestamp: 2024-10-08T15:52:50.753Z
Learning: In this project, avoid using React.FC due to concerns highlighted in facebook/create-react-app#8177.


Learnt from: junhaoliao
PR: #74
File: new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomListItem.tsx:31-31
Timestamp: 2024-09-28T01:15:34.533Z
Learning: In this project, avoid using React.FC due to concerns highlighted in facebook/create-react-app#8177.


</details>
<details>
<summary>src/stores/logFileStore.ts (10)</summary>

Learnt from: zzxthehappiest
PR: #286
File: src/components/Editor/index.tsx:138-139
Timestamp: 2025-06-01T13:40:12.222Z
Learning: In the yscope-log-viewer codebase, when using Zustand stores in React components, the preferred pattern is to use getState() for static setters that never change (like setLogEventNum) to avoid unnecessary subscriptions, while using hooks for actions that do more than just setting values. All store state variables should be declared at the beginning of the component for consistency and clear dependency overview.


Learnt from: junhaoliao
PR: #224
File: src/contexts/states/viewStore.ts:108-109
Timestamp: 2025-05-09T01:07:32.803Z
Learning: For the viewStore in the yscope-log-viewer project, it's more appropriate to suppress the max-lines-per-function ESLint rule than to split the store into slices due to the tight coupling and cohesive nature of its functions.


Learnt from: junhaoliao
PR: #286
File: src/stores/viewStore.ts:46-46
Timestamp: 2025-05-21T20:08:19.249Z
Learning: For Zustand store actions, use setX for functions that simply change the value of a state, and use updateX for functions that do more than just set a value (e.g., perform additional logic, make API calls, update multiple states).


Learnt from: junhaoliao
PR: #286
File: src/stores/viewStore.ts:46-46
Timestamp: 2025-05-21T20:08:19.249Z
Learning: For Zustand store actions, use setX for functions that simply change the value of a state, and use updateX for functions that do more than just set a value (e.g., perform additional logic, make API calls, update multiple states).


Learnt from: davemarco
PR: #76
File: new-log-viewer/src/services/fileManager/utils.ts:43-59
Timestamp: 2024-09-25T21:11:48.621Z
Learning: Avoid commenting on code that is only being moved and not modified in the PR.


Learnt from: davemarco
PR: #76
File: new-log-viewer/src/services/fileManager/utils.ts:43-59
Timestamp: 2024-10-08T15:52:50.753Z
Learning: Avoid commenting on code that is only being moved and not modified in the PR.


Learnt from: junhaoliao
PR: #224
File: src/contexts/states/logFileStore/loadSlice.ts:1-1
Timestamp: 2025-04-28T08:21:28.568Z
Learning: When importing from npm packages, use the public API entrypoint (e.g., import {StateCreator} from "zustand") rather than deep-linking into package internals (e.g., import {StateCreator} from "zustand/index"), as internal file structures may change between package versions.


Learnt from: junhaoliao
PR: #265
File: src/stores/logFileStore.ts:125-125
Timestamp: 2025-05-17T05:13:39.947Z
Learning: When refactoring Zustand state imports to prevent circular dependencies in the yscope-log-viewer project, the preferred pattern is to move state retrievals closest to where they're actually used rather than retrieving them at the beginning of the function.


Learnt from: davemarco
PR: #76
File: new-log-viewer/src/utils/actions.ts:105-133
Timestamp: 2024-09-25T21:12:28.732Z
Learning: In the actions.ts file, the PAGE_TOP and PAGE_BOTTOM actions are not intended to be handled in the getPageNumCursorArgs function.


Learnt from: davemarco
PR: #76
File: new-log-viewer/src/utils/actions.ts:105-133
Timestamp: 2024-10-08T15:52:50.753Z
Learning: In the actions.ts file, the PAGE_TOP and PAGE_BOTTOM actions are not intended to be handled in the getPageNumCursorArgs function.


</details>

</details><details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>src/stores/logFileStore.ts (5)</summary><blockquote>

<details>
<summary>src/services/LogFileManager/index.ts (2)</summary>

* `create` (168-193)
* `cursor` (496-518)

</details>
<details>
<summary>src/typings/worker.ts (2)</summary>

* `FileSrcType` (111-111)
* `CursorType` (110-110)

</details>
<details>
<summary>src/typings/states.ts (1)</summary>

* `UI_STATE` (106-106)

</details>
<details>
<summary>src/utils/url.ts (1)</summary>

* `updateWindowUrlSearchParams` (337-337)

</details>
<details>
<summary>src/typings/url.ts (1)</summary>

* `SEARCH_PARAM_NAMES` (38-38)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>src/stores/logFileStore.ts (2)</summary>

`101-101`: **LGTM! Simplified store creation signature.**

Removing the `get` parameter from the store creation function aligns with the optimization objective since the individual setter methods are no longer needed.

---

`107-115`: **Good consolidation of state updates.**

The consolidated `set()` call efficiently updates multiple properties in a single operation, which is more performant than individual setter calls.

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=y-scope/yscope-log-viewer&utm_content=334):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@Henry8192 Henry8192 marked this pull request as ready for review June 25, 2025 17:42
@Henry8192 Henry8192 requested a review from a team as a code owner June 25, 2025 17:42
@Henry8192 Henry8192 requested a review from junhaoliao June 25, 2025 17:42
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1dae6aa and 07014f3.

📒 Files selected for processing (8)
  • src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx (3 hunks)
  • src/components/CentralContainer/Sidebar/index.tsx (1 hunks)
  • src/components/DropFileContainer/index.tsx (4 hunks)
  • src/components/MenuBar/ExportLogsButton.tsx (1 hunks)
  • src/components/MenuBar/index.tsx (2 hunks)
  • src/components/PopUps/PopUpMessageBox.tsx (3 hunks)
  • src/stores/logFileStore.ts (1 hunks)
  • src/stores/viewStore/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.

**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Prefer false == <expression> rather than !<expression>.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • src/components/CentralContainer/Sidebar/index.tsx
  • src/components/MenuBar/index.tsx
  • src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx
  • src/components/DropFileContainer/index.tsx
  • src/components/MenuBar/ExportLogsButton.tsx
  • src/components/PopUps/PopUpMessageBox.tsx
  • src/stores/viewStore/index.ts
  • src/stores/logFileStore.ts
🧠 Learnings (1)
src/components/CentralContainer/Sidebar/index.tsx (1)
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#94
File: new-log-viewer/src/components/CentralContainer/Sidebar/index.tsx:99-118
Timestamp: 2024-10-19T03:33:29.578Z
Learning: In `new-log-viewer/src/components/CentralContainer/Sidebar/index.tsx`, when using `useEffect` to register window resize event handlers, it's acceptable to have an empty dependency array because the variables and functions used within the effect (`getPanelWidth`, `PANEL_CLIP_THRESHOLD_IN_PIXELS`, and `tabListRef`) are constants or refs declared outside the functional component and do not change.
🔇 Additional comments (17)
src/stores/logFileStore.ts (2)

101-101: LGTM: Store creation signature simplified appropriately.

Removing the unused get parameter from the store creation function is a good cleanup that aligns with the refactoring to use direct set() calls instead of individual setter methods.


107-109: LGTM: Direct state updates improve performance.

The refactor to use direct set() calls with partial state updates instead of individual setter methods is more efficient and follows zustand best practices. This eliminates the overhead of maintaining separate setter functions.

src/components/CentralContainer/Sidebar/index.tsx (1)

52-55: LGTM: Imperative store access prevents stale closures.

The refactor to access setActiveTabName directly from useUiStore.getState() inside the callback is correct. This ensures the latest store method is always used and eliminates the need to include it in the dependency array, making the callback more stable.

src/components/MenuBar/ExportLogsButton.tsx (1)

39-39: LGTM: Direct store method access optimizes performance.

Accessing exportLogs directly via useLogExportStore.getState().exportLogs in the onClick handler is correct. This avoids reactive subscriptions to the method and ensures the latest store method is used when the button is clicked.

src/components/MenuBar/index.tsx (1)

35-40: LGTM: Memoization with imperative store access is well-implemented.

The combination of useCallback with direct store access via useLogFileStore.getState().loadFile is excellent. The empty dependency array ensures the callback remains stable while the imperative access guarantees the latest store method is used when opening files.

src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx (1)

54-67: LGTM: Correct memoization with appropriate dependencies.

The useCallback implementation with [activeTabName] dependency is correct since the callback uses this value in its toggle logic. Accessing setActiveTabName directly from useUiStore.getState() ensures the latest store method is used while maintaining proper memoization.

src/components/DropFileContainer/index.tsx (3)

1-4: LGTM: Import addition is appropriate.

Adding useCallback to the React imports aligns with the memoization pattern applied to the event handlers below.


31-52: LGTM: Proper memoization with appropriate dependencies.

The handleDrag callback is correctly memoized with an empty dependency array since it only uses setIsFileHovering (which is stable from useState) and event properties.


54-71: Excellent refactoring: Direct store access improves performance.

The refactoring from hook-based subscription to direct getState() access is well-implemented:

  • useCallback correctly includes disabled as a dependency
  • loadFile is accessed only when needed, reducing unnecessary re-renders
  • The pattern aligns with the PR's objective of optimizing zustand action calls
src/components/PopUps/PopUpMessageBox.tsx (3)

2-2: LGTM: Import addition supports callback memoization.

Adding useCallback to support the memoization pattern applied below.


46-49: Excellent optimization: Direct store access in memoized callback.

The refactoring properly:

  • Uses useCallback with correct id dependency
  • Accesses handlePopUpMessageClose directly via getState() when needed
  • Eliminates unnecessary component re-renders from store subscriptions

69-69: Consistent pattern: Direct store access in effect.

Good consistency applying the same direct access pattern within the useEffect, maintaining the optimization benefits throughout the component.

src/stores/viewStore/index.ts (5)

2-5: LGTM: Appropriate imports for persistence functionality.

The imports correctly bring in the necessary zustand persistence middleware components.


7-7: LGTM: Error handling import for async operations.

Importing error handling for the async rehydration operation is a good practice.


12-16: LGTM: Well-defined types for URL persistence.

The type definitions properly constrain the persisted state to the relevant URL hash parameters.

Also applies to: 19-19


42-59: LGTM: Proper store configuration with persistence.

The store configuration correctly:

  • Uses the persist middleware wrapper
  • Properly partializes state to only persist relevant fields
  • Applies the custom hash storage adapter
  • Maintains the original store structure

22-40: Verify URL manipulation security and consider edge cases.

The custom storage implementation looks functional but has potential concerns:

  1. URL Length Limits: Large logEventNum values could exceed URL length limits
  2. Invalid Hash Handling: Consider what happens with malformed hash parameters
  3. Race Conditions: Multiple rapid hash changes might cause issues
#!/bin/bash
# Check if there are any URL validation utilities or constants in the codebase
rg -A 5 -B 5 "URL.*valid|hash.*valid|URLSearchParams.*valid"

# Look for existing URL parameter handling patterns
rg -A 10 "URLSearchParams|location\.hash"

# Check for any URL length or validation constants
rg -A 5 "URL.*LENGTH|MAX.*URL|hash.*length"

Comment on lines 61 to 65
window.addEventListener("hashchange", () => {
(async () => {
await useViewStore.persist.rehydrate();
})().catch(handleErrorWithNotification);
});
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider cleanup and error boundary for event listener.

The hashchange listener implementation is functional but could be improved:

  1. Memory Leak Prevention: The event listener is never removed
  2. Component Lifecycle: Consider if this should be tied to component mounting/unmounting

Consider wrapping this in a cleanup mechanism:

+let isListenerAttached = false;
+
+export const attachHashListener = () => {
+    if (isListenerAttached) return;
+    
+    const handleHashChange = () => {
+        (async () => {
+            await useViewStore.persist.rehydrate();
+        })().catch(handleErrorWithNotification);
+    };
+    
+    window.addEventListener("hashchange", handleHashChange);
+    isListenerAttached = true;
+    
+    return () => {
+        window.removeEventListener("hashchange", handleHashChange);
+        isListenerAttached = false;
+    };
+};

-window.addEventListener("hashchange", () => {
-    (async () => {
-        await useViewStore.persist.rehydrate();
-    })().catch(handleErrorWithNotification);
-});
🤖 Prompt for AI Agents
In src/stores/viewStore/index.ts around lines 61 to 65, the hashchange event
listener is added but never removed, risking memory leaks. Refactor the code to
add the event listener when the component or module initializes and remove it
during cleanup or unmounting. Implement a cleanup function that removes the
event listener to prevent memory leaks and ensure proper lifecycle management.

Copy link
Member

@junhaoliao junhaoliao left a comment

Choose a reason for hiding this comment

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

for the PR title, how about:

Optimize React hook dependencies by using getState() for immutable Zustand store actions (fixes #299).

@Henry8192 Henry8192 changed the title refactor: Use useState for calling zustand actions (fixes #299). refactor: Use useState for calling zustand actions (fixes #299); Remove unused setters from logFileStore. Jun 26, 2025
@Henry8192 Henry8192 changed the title refactor: Use useState for calling zustand actions (fixes #299); Remove unused setters from logFileStore. refactor: Optimize React hook dependencies by using getState() for immutable Zustand store actions (fixes #299); Remove unused setters from logFileStore. Jun 26, 2025
@Henry8192 Henry8192 requested a review from junhaoliao June 26, 2025 22:37
@junhaoliao junhaoliao changed the title refactor: Optimize React hook dependencies by using getState() for immutable Zustand store actions (fixes #299); Remove unused setters from logFileStore. refactor: Optimize React hook dependencies by using getState() for immutable Zustand store actions (fixes #299); Remove unused setters from logFileStore. Jun 26, 2025
@Henry8192 Henry8192 merged commit 07cf097 into y-scope:main Jun 27, 2025
6 of 7 checks passed
@Henry8192 Henry8192 deleted the refactor-zustand-actions-usage branch June 27, 2025 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

use getState() for immutable Zustand actions

2 participants