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

Task: Migrate wp-data stores from registerStore to createReduxStore #74399

Open
16 of 40 tasks
noahtallen opened this issue Mar 13, 2023 · 12 comments
Open
16 of 40 tasks

Task: Migrate wp-data stores from registerStore to createReduxStore #74399

noahtallen opened this issue Mar 13, 2023 · 12 comments
Assignees

Comments

@noahtallen
Copy link
Contributor

noahtallen commented Mar 13, 2023

Details

The main reason we should do this is that registerStore is deprecated. But the main benefit we get is the new approach to using stores gives us much better typescript integration. Previously, one would have to declare types to override the entire wp data package.

This is actually impossible to do now that the package provides its own typedefs, so we've had to rely on the temporary solution of casting to selector types like (select( 'automattic/foo' ) as FooSelectors).selector(). This is very clunky, and we also lost type info for dispatch calls. (See this comment for more info.)

As a result, we should migrate stores to use the "new" format (it's been available for a long time actually, though it wasn't supported by the WordPress versions we support at first.) And ideally, also start using stores via imports instead of via string names!

This will restore type data we lost in the upgrade, and also make types much easier to work with for WordPress data.

This is all made possible by our recent WordPress package updates done in #73890.

Checklist

A store is registered in each of these files. See PRs for example migrations.

Editing Toolkit Plugin

Calypso data-stores

Payments related

  • client/state/partner-portal/credit-card-form/index.ts @tyxla Data Stores: Refactor partner portal credit card store to createReduxStore() #74656
  • client/my-sites/checkout/composite-checkout/hooks/wpcom-store.ts Checkout: Migrate checkout data store to createReduxStore #74452
  • client/my-sites/checkout/composite-checkout/payment-methods/netbanking.tsx
  • client/my-sites/checkout/composite-checkout/payment-methods/paypal.tsx
  • client/my-sites/checkout/composite-checkout/payment-methods/credit-card/store.ts
  • client/my-sites/checkout/composite-checkout/payment-methods/wechat/index.tsx
  • packages/wpcom-checkout/src/payment-methods/alipay.tsx
  • packages/wpcom-checkout/src/payment-methods/bancontact.tsx
  • packages/wpcom-checkout/src/payment-methods/eps.tsx
  • packages/wpcom-checkout/src/payment-methods/giropay.tsx
  • packages/wpcom-checkout/src/payment-methods/ideal.tsx
  • packages/wpcom-checkout/src/payment-methods/p24.tsx
  • packages/wpcom-checkout/src/payment-methods/sofort.tsx

On hold:

These two persist a single option to help keep a modal close. Could probably be migrated to wp preferences.

These data stores persist several options. Since these persist much more of the store, it'd be harder to migrate them to wp preferences.

Address this after double registration issue is solved:

  • Follow-up task: once the double-registration problem is solved, simplify how ETK imports stores across different bundles. (currently it uses string-based store keys)
@tyxla
Copy link
Member

tyxla commented Mar 14, 2023

Thanks for opening an issue, @noahtallen!

I and the rest of @Automattic/team-calypso-frameworks would love to aid with those.

Perhaps @sirbrillig and the rest of @Automattic/shilling folks could aid with the checkout-related ones?

@sirbrillig
Copy link
Member

I'm curious: in my testing, while passing the store to useSelect does correctly provide types, passing the store to useDispatch always seems to return any. Is that expected or am I doing something wrong?

@tyxla
Copy link
Member

tyxla commented Mar 15, 2023

I'm curious: in my testing, while passing the store to useSelect does correctly provide types, passing the store to useDispatch always seems to return any. Is that expected or am I doing something wrong?

This is expected, @sirbrillig, because useDispatch types were added later in WordPress/gutenberg#43528.

We'll improve the situation with useDispatch types as soon as we upgrade to a newer version of the @wordpress packages, which is on our radar.

@sirbrillig
Copy link
Member

Sounds good! I just wanted to make sure I was doing it right!

@tyxla
Copy link
Member

tyxla commented Mar 15, 2023

Many of the usages of those stores are in Gutenboarding, which I found to be unused. See #74475 where I explain in detail and suggest its removal.

Landing that will make part of the work refactoring those stores easier, since some may end up unused.

@noahtallen
Copy link
Contributor Author

noahtallen commented Mar 15, 2023

I ran into an issue with block-editor-nux (#74454). Essentially, with the way the wp data persistence plugin works, it only works if you use registerStore and not the other methods. However, there has been a lot of work and deprecation around the plugin layer in general, so there's not a straightforward solution. Hopefully we can have a conversation about that in core here: WordPress/gutenberg#11449

@noahtallen
Copy link
Contributor Author

In this PR, there are suggestions that using the old registration approach for data stores improves tree shaking. Thoughts?

@tyxla
Copy link
Member

tyxla commented Mar 17, 2023

In this PR, there are suggestions that using the old registration approach for data stores improves tree shaking. Thoughts?

I'm not concerned about this for a few reasons:

  • Calypso is far from being 100% ESM, and that prevents webpack from properly being able to identify modules as side-effect-free. That means that for any module that we don't specifically mark as side-effect-free (through "sideEffects": false in a corresponding package.json file), tree shaking won't really work. We don't follow that practice often in Calypso, therefore we don't leverage tree shaking properly either. With that in mind, what difference does this make? Nothing measurable from what I can expect.
  • The additional register() function is an additional layer of indirection, yet another abstraction, which I'm not sure is justified.
  • With the register() function we had to check the store for existence before registration. That pattern to me was yet another code smell and indication that we're likely not doing something properly, especially given that this pattern doesn't exist in Gutenberg.
  • The additional register() function felt like a naming conflict. Is this our register() data store registration function? Is it @wordpress/data's register() function? One would have to look at the imports to be sure.
  • The new approach follows the core Gutenberg patterns for store registration. If we discover a better approach for store registration, I'd expect that would come from there first, and then would be adopted from Calypso, not the other way around. Ideally any research, discovery and new feature work in data and data store registration APIs should be done there, and then we should follow suite in Calypso.
  • The new approach is simpler and more straightforward and allows type inference without the additional magic that was required before. Given that it's the default way of registering stores in Gutenberg, I only expect type support will even improve over time, like it historically did with adding types for useDispatch() a few months ago.

@noahtallen
Copy link
Contributor Author

Nice explanation, thanks! One related thought I had is that importing anything from @automattic/data-stores probably registers every single store in the package, given that the file import in the top-level index file of that package will cause every store to register. This didn't happen before. I don't immediately have any concerns about it, though.

@tyxla
Copy link
Member

tyxla commented Mar 20, 2023

Nice explanation, thanks! One related thought I had is that importing anything from @automattic/data-stores probably registers every single store in the package, given that the file import in the top-level index file of that package will cause every store to register. This didn't happen before. I don't immediately have any concerns about it, though.

That's a valid one, indeed. I don't very much like the idea of re-exporting everything through the main package anyway, especially given that we have side effects. One way to beat that would be to not use a primary index and re-export there, but rather to import from the specific stores only, using bare module specifier resolution:

import { AutomatedTransferEligibility } from '@automattic/data-stores/automated-transfer-eligibility';

@jsnajdr
Copy link
Member

jsnajdr commented Mar 20, 2023

importing anything from @automattic/data-stores probably registers every single store in the package

The every-store-registration shouldn't be happening. The data-stores package has the sideEffects: false flag enabled, and when there are re-exports like this in data-stores/index.js:

import * as Foo from './foo';
import * as Bar from './bar';

export { Foo, Bar };

Then webpack will load ./foo only if its exports are actually imported anywhere. Reexports are "pass-through", i.e., if some useFoo.js file imports Foo:

import { Foo } from 'data-stores';

webpack will not consider the intermediate index.js file, and will treat it as "useFoo.js is importing Foo from data-stores/foo.js". Cut out the middleman!

The caveat is that the Foo store will be registered only if Foo is imported anywhere. In other words, you are kind of forced to use the new store description convention:

import { Foo } from 'data-stores';

registry.select( Foo.store ).getFoo();

@noahtallen
Copy link
Contributor Author

In other words, you are kind of forced to use the new store description convention

Nice, thanks for the explanation. This side effect stuff can get tricky :)

We are migrating how these stores are used at the same time. Previously, you still needed to import Foo store and register it somewhere in the app. In most cases, the consumer also re-exported the store key and then used that to access the stores. So it's not a massive change -- just removing one or two layers of indirection

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants