Skip to content

Fix race condition for woocommerce_admin_reports_list filter #59897

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 8, 2025

Conversation

chihsuan
Copy link
Member

@chihsuan chihsuan commented Jul 23, 2025

Submission Review Guidelines:

Changes proposed in this Pull Request:

This PR addresses a race condition issue with the woocommerce_admin_reports_list filter, similar to the one that was previously fixed for the woocommerce_admin_pages_list filter in #47696 and #58834.

Problem: Reports registered via the woocommerce_admin_reports_list filter after the initial application load were not being recognized by the routing system and breadcrumb generation.

Solution: Implement the same race condition mitigation pattern used for pages, providing reactive updates when new reports are added to the filter.

Changes made:

  • Renamed get-reports.js to use-reports.js to better reflect its purpose
  • Added useReports hook with race condition mitigation that watches for new hooks
  • Updated controller.js to use useReports in the usePages hook
  • Added withReports HOC to report/index.js for class component compatibility
  • Maintained backward compatibility with the existing getReports function

How to test the changes in this Pull Request:

Using the WooCommerce Testing Instructions Guide, include your detailed testing instructions:

  1. Install and activate
    test-reports-race-condition.php.zip plugin that registers reports via the woocommerce_admin_reports_list filter with delayed registration.
  2. Use trunk branch
  3. Navigate to the /wp-admin/admin.php?page=wc-admin&path=%2Fanalytics%2Ftest-report-delayed-3s
  4. Verify that the Sorry, you are not allowed to access this page. message is displayed
  5. Check out this branch
  6. Refresh the page and verify that the Test Report (3s Delay) is displayed
  7. Verify that existing core reports continue to work as expected
Screenshot 2025-07-23 at 11 09 35 Screenshot 2025-07-23 at 11 09 46

Changelog entry

  • Automatically create a changelog entry from the details below.
Changelog Entry Details

Significance

  • Patch

Type

  • Fix - Fixes an existing bug

Message

Fix race condition for woocommerce_admin_reports_list filter, preventing dynamically registered reports from being recognized

Changelog Entry Comment

Comment

Addresses the race condition issue where reports registered via the
woocommerce_admin_reports_list filter after initial application load
were not being recognized. This follows the same pattern used for the
woocommerce_admin_pages_list filter fix.

Changes:
- Rename get-reports.js to use-reports.js for clearer purpose
- Add useReports hook with race condition mitigation
- Update controller.js to use useReports in usePages
- Add withReports HOC to report/index.js for class component compatibility
- Maintain backward compatibility with existing getReports function

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions bot added the plugin: woocommerce Issues related to the WooCommerce Core plugin. label Jul 23, 2025
Copy link
Contributor

coderabbitai bot commented Jul 23, 2025

📝 Walkthrough

Walkthrough

The changes refactor how analytics reports are sourced and consumed in the WooCommerce admin client. They replace a static function-based approach with a React hook (useReports) for obtaining reports, introduce a higher-order component to inject reports as props, and update related components and hooks to use this new reactive data flow. Additionally, a patch fixes a race condition related to dynamic report registration. A new utility hook useFilterHook is added to handle WordPress filter hooks reactively and mitigate race conditions.

Changes

File(s) Change Summary
plugins/woocommerce/client/admin/client/analytics/report/index.js Replaced direct report fetching with a useReports-powered HOC; updated Report component to use injected reports prop; modified export composition.
plugins/woocommerce/client/admin/client/analytics/report/use-reports.js Changed default export to named getReports; added and exported useReports hook using useFilterHook for reactive filtered reports.
plugins/woocommerce/client/admin/client/layout/controller.js Switched from static getReports to useReports hook; updated getPages to accept reports parameter; refactored usePages to use useReports and useFilterHook; removed unused imports.
plugins/woocommerce/client/admin/client/utils/use-filter-hook.ts Added new React hook useFilterHook to manage WordPress filter hooks reactively and mitigate race conditions via hookAdded event listener.
plugins/woocommerce/changelog/59897-fix-reports-race-condition Added patch to fix race condition affecting dynamic report registration and recognition.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Report Component
    participant HOC as withReports HOC
    participant Hook as useReports Hook
    participant Data as Reports Data Source

    UI->>HOC: Render (wrapped by withReports)
    HOC->>Hook: Call useReports()
    Hook->>Data: Fetch reports (getReports)
    Data-->>Hook: Return current reports array
    Hook-->>HOC: Provide reports array
    HOC-->>UI: Inject reports as prop
    UI->>UI: Use reports prop for rendering
    Note over Hook,Data: On new report hooks, useReports updates reports and triggers re-render
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~45 minutes

Possibly related PRs

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01780d2 and b36a6a0.

📒 Files selected for processing (1)
  • plugins/woocommerce/client/admin/client/utils/use-filter-hook.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugins/woocommerce/client/admin/client/utils/use-filter-hook.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Core e2e tests 3/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 6/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Metrics - @woocommerce/plugin-woocommerce [performance]
  • GitHub Check: Core e2e tests 4/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 2/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 5/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 1/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: JavaScript - @woocommerce/admin-library [unit]
  • GitHub Check: Lint - @woocommerce/admin-library
  • GitHub Check: build
  • GitHub Check: Check Asset Sizes
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/reports-race-condition

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • 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 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 for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for 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.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

github-actions bot commented Jul 23, 2025

Test using WordPress Playground

The changes in this pull request can be previewed and tested using a WordPress Playground instance.
WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Test this pull request with WordPress Playground.

Note that this URL is valid for 30 days from when this comment was last updated. You can update it by closing/reopening the PR or pushing a new commit.

Copy link
Contributor

github-actions bot commented Jul 23, 2025

Size Change: +2.72 kB (+0.05%)

Total Size: 5.91 MB

Filename Size Change
./plugins/woocommerce/client/admin/build/email-editor/index.js 27.2 kB +352 B (+1.31%)
./plugins/woocommerce/client/admin/build/wp-admin-scripts/email-editor-integration.js 30.8 kB +364 B (+1.19%)
./packages/js/email-editor/build/hooks/use-remove-saving-failed-notices.js 756 B +756 B (new file) 🆕
./packages/js/email-editor/build/middleware/content-validation.js 1.07 kB +1.07 kB (new file) 🆕

compressed-size-action

@chihsuan chihsuan closed this Jul 23, 2025
@chihsuan chihsuan reopened this Jul 23, 2025
@chihsuan chihsuan self-assigned this Jul 23, 2025
@chihsuan chihsuan changed the title Fix race condition for woocommerce_admin_reports_list filter Fix race condition for woocommerce_admin_reports_list filter Jul 23, 2025
@chihsuan chihsuan requested review from joshuatf and louwie17 July 23, 2025 03:12
Copy link
Contributor

github-actions bot commented Jul 23, 2025

Testing Guidelines

Hi @louwie17 @joshuatf ,

Apart from reviewing the code changes, please make sure to review the testing instructions (Guide) and verify that relevant tests (E2E, Unit, Integration, etc.) have been added or updated as needed.

Reminder: PR reviewers are required to document testing performed. This includes:

  • 🖼️ Screenshots or screen recordings.
  • 📝 List of functionality tested / steps followed.
  • 🌐 Site details (environment attributes such as hosting type, plugins, theme, store size, store age, and relevant settings).
  • 🔍 Any analysis performed, such as assessing potential impacts on environment attributes and other plugins, conducting performance profiling, or using LLM/AI-based analysis.

⚠️ Within the testing details you provide, please ensure that no sensitive information (such as API keys, passwords, user data, etc.) is included in this public issue.

Copy link
Contributor

@joshuatf joshuatf left a comment

Choose a reason for hiding this comment

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

Tested and works well for me:

  • ✅ Got the "no match" component on trunk
  • ✅ Retrieved the late loaded page on this branch
  • ❓ Left a question about creating a wrapper hook so we can avoid this scenario in the future
Screenshot 2025-07-24 at 7 04 37 AM

useEffect( () => {
const handleHookAdded = ( hookName ) => {
if (
hookName === REPORTS_FILTER &&
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a neat workaround! It does make me wonder, especially since we're using this pattern multiple times, if we should turn this into a hook wrapper when we want to re-render content.

const applyFiltersHook = ( hookName, ...args ) => {
   const [ hookValue, setHookValue ] = useState( null );
    if (
		hookName === hookName &&
		didFilter( hookName ) > 0
	) {
        // Recalculate hook value here.
		setHookValue( value );
	}

    return hookValue;
}

I'm not sure if there are issues with this though. I'm really surprised there's not an upstream fix for this since it's such a common pattern.

Copy link
Member Author

Choose a reason for hiding this comment

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

Great suggestion! I've implemented the reusable hook wrapper approach you suggested in 73bb853.

chihsuan added 2 commits July 25, 2025 13:30
…e condition mitigation

- Simplified useReports and usePages hooks to leverage the new useFilterHook utility, improving race condition handling for dynamically added reports and pages.
- Removed redundant state management and effects in favor of a more generic solution.
- Introduced useFilterHook to encapsulate filter logic and ensure components re-render correctly when new hooks are added.
Copy link
Contributor

@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: 3

🧹 Nitpick comments (1)
plugins/woocommerce/client/admin/client/utils/use-filter-hook.ts (1)

50-51: Add unit tests and improve robustness

The hook needs unit tests as required by the coding guidelines, and should be more defensive about the WordPress hooks API availability.

+// Add type guard for better TypeScript support
+if (typeof addAction === 'undefined' || typeof removeAction === 'undefined') {
+	console.warn('useFilterHook: WordPress hooks API not available');
+	return getterFn();
+}
+
 return value;

Do you want me to generate comprehensive unit tests for this hook? The tests should cover:

  • Normal operation with filter hooks
  • Race condition scenarios
  • Error handling in getter functions
  • Cleanup behavior
  • Edge cases with invalid inputs
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 875a769 and 73bb853.

📒 Files selected for processing (3)
  • plugins/woocommerce/client/admin/client/analytics/report/use-reports.js (3 hunks)
  • plugins/woocommerce/client/admin/client/layout/controller.js (5 hunks)
  • plugins/woocommerce/client/admin/client/utils/use-filter-hook.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • plugins/woocommerce/client/admin/client/analytics/report/use-reports.js
  • plugins/woocommerce/client/admin/client/layout/controller.js
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)

**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable

Files:

  • plugins/woocommerce/client/admin/client/utils/use-filter-hook.ts
**/*.{php,js,ts,jsx,tsx}

⚙️ CodeRabbit Configuration File

**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:

  • Guards against unexpected inputs.
  • Sanitizes and validates any potentially dangerous inputs.
  • Is backwards compatible.
  • Is readable and intuitive.
  • Has unit or E2E tests where applicable.

Files:

  • plugins/woocommerce/client/admin/client/utils/use-filter-hook.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Core e2e tests 6/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Metrics - @woocommerce/plugin-woocommerce [performance]
  • GitHub Check: Core e2e tests 5/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 4/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 3/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 2/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 1/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: JavaScript - @woocommerce/admin-library [unit]
  • GitHub Check: Lint - @woocommerce/admin-library
  • GitHub Check: build
  • GitHub Check: Check Asset Sizes
🔇 Additional comments (1)
plugins/woocommerce/client/admin/client/utils/use-filter-hook.ts (1)

4-5: LGTM - Imports are appropriate

The imports correctly bring in the necessary React and WordPress hooks APIs for the functionality.

Comment on lines +17 to +22
export function useFilterHook< T >(
filterName: string,
getterFn: () => T,
dependencies: React.DependencyList = []
): T {
const [ value, setValue ] = useState< T >( getterFn );
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation to guard against unexpected inputs

The function lacks validation for its parameters, which could lead to runtime errors:

  1. filterName should be validated as a non-empty string
  2. getterFn should be validated as a function
  3. Consider error handling if getterFn() throws an exception during initial state setup
export function useFilterHook< T >(
	filterName: string,
	getterFn: () => T,
	dependencies: React.DependencyList = []
): T {
+	if (!filterName || typeof filterName !== 'string') {
+		throw new Error('useFilterHook: filterName must be a non-empty string');
+	}
+	if (typeof getterFn !== 'function') {
+		throw new Error('useFilterHook: getterFn must be a function');
+	}
+
-	const [ value, setValue ] = useState< T >( getterFn );
+	const [ value, setValue ] = useState< T >( () => {
+		try {
+			return getterFn();
+		} catch (error) {
+			console.error(`useFilterHook: Error in getterFn for filter "${filterName}":`, error);
+			throw error;
+		}
+	});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function useFilterHook< T >(
filterName: string,
getterFn: () => T,
dependencies: React.DependencyList = []
): T {
const [ value, setValue ] = useState< T >( getterFn );
export function useFilterHook< T >(
filterName: string,
getterFn: () => T,
dependencies: React.DependencyList = []
): T {
if (!filterName || typeof filterName !== 'string') {
throw new Error('useFilterHook: filterName must be a non-empty string');
}
if (typeof getterFn !== 'function') {
throw new Error('useFilterHook: getterFn must be a function');
}
const [ value, setValue ] = useState< T >( () => {
try {
return getterFn();
} catch (error) {
console.error(
`useFilterHook: Error in getterFn for filter "${filterName}":`,
error
);
throw error;
}
});
🤖 Prompt for AI Agents
In plugins/woocommerce/client/admin/client/utils/use-filter-hook.ts around lines
17 to 22, add input validation to ensure filterName is a non-empty string and
getterFn is a function before using them. Also, wrap the call to getterFn() in a
try-catch block to handle any exceptions during initial state setup, providing
fallback behavior or error logging as needed to prevent runtime errors.

@chihsuan chihsuan requested a review from joshuatf July 28, 2025 03:15
Copy link
Contributor

@joshuatf joshuatf left a comment

Choose a reason for hiding this comment

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

Thanks for taking the time to add that hook! Still testing well for me and I'm able to see the delayed content with the latest changes. LGTM!

Screenshot 2025-08-07 at 9 55 35 AM

@chihsuan chihsuan merged commit 7840574 into trunk Aug 8, 2025
26 checks passed
@chihsuan chihsuan deleted the fix/reports-race-condition branch August 8, 2025 03:07
@github-actions github-actions bot added this to the 10.2.0 milestone Aug 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plugin: woocommerce Issues related to the WooCommerce Core plugin.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants