Skip to content
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

Jetpack: update react-router-dom from v5 to v6 #40773

Open
wants to merge 9 commits into
base: trunk
Choose a base branch
from

Conversation

tbradsha
Copy link
Contributor

This is PR bumps Jetpack-the-plugin to use react-router-dom to v6 (prior to migrating everything else in the monorepo to v6 (see #40705) and ultimately to v7).

Proposed changes:

Essentially I followed this guide:
https://reactrouter.com/6.28.0/upgrading/v5

Of note:

That last item could probably use the most scrutiny in a code review.

Other information:

  • Have you written new tests for your changes, if applicable?
  • Have you checked the E2E test CI results, and verified that your changes do not break them?
  • Have you tested your changes on WordPress.com, if applicable (if so, you'll see a generated comment below with a script to run)?

Jetpack product discussion

Does this pull request change what data or activity we track or use?

Testing instructions:

  • Verify a clickthrough works as expected.
  • Verify CI is happy.

Copy link
Contributor

github-actions bot commented Dec 30, 2024

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WordPress.com Simple site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin, and enable the update/jetpack/react-router-dom_v5_to_v6 branch.

    • For jetpack-mu-wpcom changes, also add define( 'JETPACK_MU_WPCOM_LOAD_VIA_BETA_PLUGIN', true ); to your wp-config.php file.
  • To test on Simple, run the following command on your sandbox:

    bin/jetpack-downloader test jetpack update/jetpack/react-router-dom_v5_to_v6
    
    bin/jetpack-downloader test jetpack-mu-wpcom-plugin update/jetpack/react-router-dom_v5_to_v6
    

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@github-actions github-actions bot added [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ Admin Page React-powered dashboard under the Jetpack menu labels Dec 30, 2024
Copy link
Contributor

github-actions bot commented Dec 30, 2024

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Team Review, ...).
  • ✅ Add a "[Type]" label (Bug, Enhancement, Janitorial, Task).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


The e2e test report can be found here. Please note that it can take a few minutes after the e2e tests checks are complete for the report to be available.


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Choose a review path based on your changes:
    • A. Team Review: add the "[Status] Needs Team Review" label
      • For most changes, including minor cross-team impacts.
      • Example: Updating a team-specific component or a small change to a shared library.
    • B. Crew Review: add the "[Status] Needs Review" label
      • For significant changes to core functionality.
      • Example: Major updates to a shared library or complex features.
    • C. Both: Start with Team, then request Crew
      • For complex changes or when you need extra confidence.
      • Example: Refactor affecting multiple systems.
  3. Get at least one approval before merging.

Still unsure? Reach out in #jetpack-developers for guidance!


Jetpack plugin:

The Jetpack plugin has different release cadences depending on the platform:

  • WordPress.com Simple releases happen semi-continuously (PCYsg-Jjm-p2).
  • WoA releases happen weekly.
  • Releases to self-hosted sites happen monthly. The next release is scheduled for January 7, 2025 (scheduled code freeze on undefined).

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.

Comment on lines +41 to +47
}

componentDidUpdate( oldprops ) {
// We need to handle the search term not only on route update but also on page load in case of some external redirects
if ( oldprops.location !== this.props.location ) {
this.onRouteChange( this.props.location );
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's no direct upgrade path for this, but hopefully this roughly the same thing as before.

@tbradsha tbradsha force-pushed the update/jetpack/react-router-dom_v5_to_v6 branch from 243b30a to 93b00f6 Compare December 30, 2024 22:10
Comment on lines +11 to +12
* @param {object} root0.location - location object
* @param {string} root0.location.pathname - location pathname
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of bc03437, it looks like a more "proper" way to fix the tests may be to put them inside a <MemoryRouter> that is initialized with the correct location paths, something like this

<MemoryRouter initialEntries={ [ '/whatever' ] }>
    <PlanConflictWarning />
</MemoryRouter>

Although to get it to actually work I wound up with something more like this:

/**
 * Wrap the component in a `<MemoryRouter>` for testing.
 *
 * @param {PlanConflictWarning} component The `<PlanConflictWarning>` to wrap.
 * @param {string[]} entries Initial path entries.
 * @returns {MemoryRouter} Router to render..
 */
function wrapWithMemoryRouter( component, entries = [ '/plans' ] ) {
	return (
		<MemoryRouter
			initialEntries={ entries }
			future={
				// Enable some forward-compat things so it doesn't console.warn about enabling them. Sigh.
				{ v7_startTransition: true, v7_relativeSplatPath: true }
			}
		>
			{ component }
		</MemoryRouter>
	);
}

}

componentDidUpdate( oldprops ) {
// We need to handle the search term not only on route update but also on page load in case of some external redirects
Copy link
Contributor

Choose a reason for hiding this comment

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

The bits here on line 45–47 are the "on route update", while the UNSAFE_componentWillMount() is the "also on page load". I think this comment may be in the wrong place?

@@ -854,10 +857,12 @@ class Main extends React.Component {
<AdminNotices />
<JetpackNotices />
{ this.shouldConnectUser() && this.connectUser() }
{ /* This is no longer supported as of react-router-dom v6: https://github.com/remix-run/react-router/issues/8139
Copy link
Contributor

Choose a reason for hiding this comment

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

If we really want it, I think we could use unstable_usePrompt(). It claims it can be weird in different browsers in some cases though.

For use inside a class component, like here, that might mean creating our own tiny <Prompt> component something like this:

function Prompt( { when, message } ) {
	unstable_usePrompt( { when, message } );
	return null;
}

@anomiex
Copy link
Contributor

anomiex commented Jan 6, 2025

I think I see why the E2Es are failing.

Formerly, due to lack of exact,

<Route path="/recommendations">
<Main routeName={ getRouteName( '/recommendations' ) } />
</Route>
would match anything beginning with /recommendations. But that was ok, since
<Switch>
{ /* TODO: Why we don't redirect improper step paths? */ }
<Redirect exact from={ '/recommendations' } to={ '/recommendations' + redirectPath } />
<Route path="/recommendations/site-type">
<SiteTypeQuestion />
</Route>
<Route path="/recommendations/product-suggestions">
<ProductSuggestions />
</Route>
<Route path="/recommendations/product-purchased">
<ProductPurchased />
</Route>
<Route path="/recommendations/agency">
<ResourcePrompt stepSlug="agency" />
</Route>
<Route path="/recommendations/woocommerce">
<FeaturePrompt stepSlug="woocommerce" />
</Route>
<Route path="/recommendations/monitor">
<FeaturePrompt stepSlug="monitor" />
</Route>
<Route path="/recommendations/newsletter">
<FeaturePrompt stepSlug="newsletter" />
</Route>
<Route path="/recommendations/related-posts">
<FeaturePrompt stepSlug="related-posts" />
</Route>
<Route path="/recommendations/creative-mail">
<FeaturePrompt stepSlug="creative-mail" />
</Route>
<Route path="/recommendations/site-accelerator">
<FeaturePrompt stepSlug="site-accelerator" />
</Route>
<Route path="/recommendations/vaultpress-backup">
<ResourcePrompt stepSlug="vaultpress-backup" />
</Route>
<Route path="/recommendations/vaultpress-for-woocommerce">
<ResourcePrompt stepSlug="vaultpress-for-woocommerce" />
</Route>
<Route path="/recommendations/publicize">
<FeaturePrompt stepSlug="publicize" isNew={ isNew( 'publicize' ) } />
</Route>
<Route path="/recommendations/protect">
<FeaturePrompt stepSlug="protect" isNew={ isNew( 'protect' ) } />
</Route>
<Route path="/recommendations/anti-spam">
<ResourcePrompt stepSlug="anti-spam" isNew={ isNew( 'anti-spam' ) } />
</Route>
<Route path="/recommendations/videopress">
<FeaturePrompt stepSlug="videopress" isNew={ isNew( 'videopress' ) } />
</Route>
<Route path="/recommendations/backup-plan">
<ResourcePrompt stepSlug="backup-plan" isNew={ isNew( 'backup-plan' ) } />
</Route>
<Route path="/recommendations/boost">
<FeaturePrompt stepSlug="boost" isNew={ isNew( 'boost' ) } />
</Route>
<Route path="/recommendations/welcome-backup">
<ResourcePrompt stepSlug="welcome__backup" />
</Route>
<Route path="/recommendations/welcome-complete">
<ResourcePrompt stepSlug="welcome__complete" />
</Route>
<Route path="/recommendations/welcome-starter">
<ResourcePrompt stepSlug="welcome__starter" />
</Route>
<Route path="/recommendations/welcome-security">
<ResourcePrompt stepSlug="welcome__security" />
</Route>
<Route path="/recommendations/welcome-antispam">
<ResourcePrompt stepSlug="welcome__antispam" />
</Route>
<Route path="/recommendations/welcome-videopress">
<ResourcePrompt stepSlug="welcome__videopress" />
</Route>
<Route path="/recommendations/welcome-search">
<ResourcePrompt stepSlug="welcome__search" />
</Route>
<Route path="/recommendations/welcome-scan">
<ResourcePrompt stepSlug="welcome__scan" />
</Route>
<Route path="/recommendations/welcome-social-basic">
<ResourcePrompt stepSlug="welcome__social_basic" />
</Route>
<Route path="/recommendations/welcome-social-v1">
<ResourcePrompt stepSlug="welcome__social_v1" />
</Route>
<Route path="/recommendations/welcome-social-image-generator">
<ResourcePrompt stepSlug="welcome__social_image_generator" />
</Route>
<Route path="/recommendations/welcome-golden-token">
<ResourcePrompt stepSlug="welcome__golden_token" />
</Route>
<Route path="/recommendations/backup-activated">
<ResourcePrompt stepSlug="backup-activated" />
</Route>
<Route path="/recommendations/scan-activated">
<ResourcePrompt stepSlug="scan-activated" />
</Route>
<Route path="/recommendations/unlimited-sharing-activated">
<ResourcePrompt stepSlug="unlimited-sharing-activated" />
</Route>
<Route path="/recommendations/social-v1-activated">
<ResourcePrompt stepSlug="social-v1-activated" />
</Route>
<Route path="/recommendations/antispam-activated">
<ResourcePrompt stepSlug="antispam-activated" />
</Route>
<Route path="/recommendations/videopress-activated">
<ResourcePrompt stepSlug="videopress-activated" />
</Route>
<Route path="/recommendations/search-activated">
<ResourcePrompt stepSlug="search-activated" />
</Route>
<Route path="/recommendations/server-credentials">
<ResourcePrompt stepSlug="server-credentials" />
</Route>
<Route path="/recommendations/summary">
<Summary newRecommendations={ newRecommendations } />
</Route>
</Switch>
uses absolute paths.

Now, though, we get the /recommandations route failing but the rest like /recommandations/site-type succeeding due to two things:

  1. Since it doesn't end with /*, the route at
    <Route
    path="/recommendations"
    element={ <Main routeName={ getRouteName( '/recommendations' ) } /> }
    />
    only matches /recommandations, not anything like /recommandations/site-type. The route at
    <Route
    path="/recommendations"
    element={ <Navigate to={ '/recommendations' + redirectPath } replace /> }
    />
    fails to match it, because since paths are relative now it's trying to match for /recommandations/recommendations instead.
  2. /recommandations/site-type and the like continue to work because they go via
    <Route path="/*" element={ <Main routeName={ getRouteName( '/*' ) } /> } />
    instead.

AFAICT, to fix all this we need to make that be path="/recommendations/*" in the first bit and remove the "/recommendations" prefix from everything in the second bit (which means that first entry will become path="", and the rest can apparently be either like path="site-type" or path="/site-type").

@anomiex
Copy link
Contributor

anomiex commented Jan 6, 2025

P.S. Something that's really confusing is that v6 allows absolute paths in nested routes for something like

<Routes>
  <Route path="/foo/*" element={<Foo />}>
    <Route path="/foo/bar" element={<FooBar />} />
  </Route>
</Routes>

but doesn't for a case like ours which looks more like

<Routes>
  <Route path="/foo/*" element={<Foo />} />
</Routes>

with Foo containing

<Routes>
  <Route path="/foo/bar" element={<FooBar />} />
</Routes>

So until you come across remix-run/react-router#10321 / remix-run/react-router#9841 that explicitly point this out, you're likely to find confusing results talking about it working because they only consider the first case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Admin Page React-powered dashboard under the Jetpack menu [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Status] Needs Team Review [Type] Janitorial
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants