Closed apeatling closed 4 years ago
So I spend some time figuring out how Calypso is built and how it asks Webpack split the chunks. Thankfully there is npm run analyze-bundles
to visualize the chunk size and content.
Essentially, this issue asks for the ability to reduce this part of the build.js
bundle:
There are three targets — reducers, data-layers, and selectors. I decided to tackle reducers given prior art exists (1 and 2).
To simplify the experiment, I've selected state/timezones/reducer
as the target.
I've come up with two approaches: the first one touches the bundler and has it load the reducers the async bundles asks.
diff --git a/client/devdocs/index.js b/client/devdocs/index.js
index ef26b5bcbe..3245f90f16 100644
--- a/client/devdocs/index.js
+++ b/client/devdocs/index.js
@@ -13,5 +13,9 @@ import config from 'config';
import controller from './controller';
-export default function() {
+import { combineReducers } from 'state/utils';
+import { reducers } from 'state';
+import timezones from 'state/timezones/reducer';
+
+export default function sectionEntry() {
if ( config.isEnabled( 'devdocs' ) ) {
page( '/devdocs', controller.sidebar, controller.devdocs );
@@ -35,2 +39,4 @@ export default function() {
}
}
+
+sectionEntry.reducer = combineReducers( Object.assign( {}, reducers, { timezones } ) );
diff --git a/client/state/index.js b/client/state/index.js
index d23743d11f..686d1b79db 100644
--- a/client/state/index.js
+++ b/client/state/index.js
@@ -73,5 +73,5 @@ import storedCards from './stored-cards/reducer';
import support from './support/reducer';
import terms from './terms/reducer';
-import timezones from './timezones/reducer';
+//import timezones from './timezones/reducer';
import themes from './themes/reducer';
import ui from './ui/reducer';
@@ -89,5 +89,5 @@ import config from 'config';
const extensions = combineReducers( extensionsModule.reducers() );
-const reducers = {
+export const reducers = {
analyticsTracking,
accountRecovery,
@@ -147,5 +147,5 @@ const reducers = {
support,
terms,
- timezones,
+ //timezones,
themes,
ui,
diff --git a/server/bundler/loader.js b/server/bundler/loader.js
index 4d4ad2a5de..9e117bc341 100644
--- a/server/bundler/loader.js
+++ b/server/bundler/loader.js
@@ -119,5 +119,7 @@ function splitTemplate( path, section ) {
' controller.setSection( ' + sectionString + ' )( context );',
' if ( ! _loadedSections[ ' + moduleString + ' ] ) {',
- ' require( ' + moduleString + ' )( controller.clientRouter );',
+ ' var module = require( ' + moduleString + ' );',
+ ' module( controller.clientRouter );',
+ ' module.reducer && context.store.replaceReducer( module.reducer );',
' _loadedSections[ ' + moduleString + ' ] = true;',
' }',
This is a fragile setup because we are asking everyone annotate the top level section (i.e. devdocs
) to ensure their React componments work correctly. So I decided to implement something saner instead. This diff implements a globally-available injectReducers
and use it in QueryTimezones
:
diff --git a/client/components/data/query-timezones/index.jsx b/client/components/data/query-timezones/index.jsx
index b3cf286440..0a8ccdae1a 100644
--- a/client/components/data/query-timezones/index.jsx
+++ b/client/components/data/query-timezones/index.jsx
@@ -13,4 +13,8 @@ import { connect } from 'react-redux';
*/
import { requestTimezones } from 'state/timezones/actions';
+import timezonesReducer from 'state/timezones/reducer';
+import { injectReducers } from 'state/reducer-inject';
+
+injectReducers( { timezones: timezonesReducer } );
export class QueryTimezones extends Component {
diff --git a/client/state/index.js b/client/state/index.js
index d23743d11f..07c9846a70 100644
--- a/client/state/index.js
+++ b/client/state/index.js
@@ -62,4 +62,5 @@ import purchases from './purchases/reducer';
import reader from './reader/reducer';
import receipts from './receipts/reducer';
+import { setInitialReducers, reducerInjecter } from './reducer-inject';
import sharing from './sharing/reducer';
import shortcodes from './shortcodes/reducer';
@@ -73,5 +74,5 @@ import storedCards from './stored-cards/reducer';
import support from './support/reducer';
import terms from './terms/reducer';
-import timezones from './timezones/reducer';
+//import timezones from './timezones/reducer';
import themes from './themes/reducer';
import ui from './ui/reducer';
@@ -89,4 +90,6 @@ import config from 'config';
const extensions = combineReducers( extensionsModule.reducers() );
+const placeholderReducer = ( state = {} ) => state;
+
const reducers = {
analyticsTracking,
@@ -147,5 +150,10 @@ const reducers = {
support,
terms,
- timezones,
+ // XXX Improve this without referencing the reducer in the build chunk.
+ timezones: combineReducers( {
+ rawOffsets: placeholderReducer,
+ labels: placeholderReducer,
+ byContinents: placeholderReducer,
+ } ),
themes,
ui,
@@ -157,4 +165,5 @@ const reducers = {
export const reducer = combineReducers( reducers );
+setInitialReducers( reducers );
/**
@@ -201,4 +210,5 @@ export function createReduxStore( initialState = {} ) {
const enhancers = [
isBrowser && window.app && window.app.isDebug && consoleDispatcher,
+ isBrowser && config.isEnabled( 'code-splitting' ) && reducerInjecter,
applyMiddleware( ...middlewares ),
isBrowser && sitesSync,
diff --git a/client/state/reducer-inject/index.js b/client/state/reducer-inject/index.js
new file mode 100644
index 0000000000..920ed00589
--- /dev/null
+++ b/client/state/reducer-inject/index.js
@@ -0,0 +1,44 @@
+/**
+ * Reducer injecter Redux store enhancer
+ *
+ * `reducerInjecter` injects into the `createStore` enhancer chain
+ * in order to provide a globally available method to update the reducer
+ * from newly loaded chunks.
+ */
+
+/**
+ * External dependencies
+ */
+import { forIn } from 'lodash';
+
+/**
+ * Internal dependencies
+ */
+import { combineReducers } from 'state/utils';
+
+let store;
+let currentReducers;
+
+export const setInitialReducers = ( reducers ) => {
+ currentReducers = reducers;
+};
+
+export const injectReducers = ( reducers ) => {
+ let updated = false;
+
+ forIn( reducers, ( reducer, key ) => {
+ if ( currentReducers[ key ] && currentReducers[ key ] === reducer ) {
+ return
+ }
+
+ currentReducers[ key ] = reducer;
+ updated = true;
+ } );
+
+ updated && store.replaceReducer( combineReducers( currentReducers ) );
+};
+
+export const reducerInjecter = next => ( reducer, initialState ) => {
+ store = next( reducer, initialState );
+ return store;
+};
state/timezone
is being removed nicely; see if you could spot the difference :)
There are some improvements to be made here before this can be pushed as a pull request:
XXX
is marked in the second patch though: Without it we will likely throw out the presistent initial state. Something needs to be done to generate these placeholder reducers effectively.store
and currentReducers
globally, which is not a good pratice but I am running out of ideas. I don't know if we care.injectReducers
won't degrade the performance of the app.@ehg @apeatling Let me know what you think, especially on the proposed approach and the scope. Thanks.
Surprising, selectors can easily be removed by making sure transform-imports works. A change is proposed; that cross out one item out of three easily here :) .
It does open some possibility on whether or not we could leverage transform-imports for data-layers and/or reducers. Further investigation is needed.
One point of concern could be data dependencies, do we need some reducers before others?
It would be tedious to manually move every reducer
Maybe a codemod could make this easier? https://github.com/Automattic/wp-calypso/tree/master/bin/codemods
Looping @coderkevin, @lamosty and @jsnajdr in :)
It does open some possibility on whether or not we could leverage transform-imports for data-layers and/or reducers. Further investigation is needed.
I've given more thought to my previous comment: Even with tools like codemods, It is indeed tedious to have more async loader calls in the components, so perhaps we can do better by containing these complexities within client/state
.
Leveraging the unidirectional data flow, given that reducers are responsible for reducing the actions dispatched, as long as the subset of the reducers and data-layer is loaded for the specific action, the action should be dealt correctly, i.e. reducing to the correct set of state changes and trigger the correct set of data-layer actions.
Locate the right reducers or data-layers can be as easy as locating the files that reference a specific action type constant. Or not.
I wonder if it's possible to reuse transform-imports in some way to dissemble action types/data-layer/ reducers into bundle chunks. I should spend time experimenting on this.
(Sorry if I over-communicated and thinking out loud here. I want to make sure I don't hit any walls that have been tried already.)
Hello @timdream! Thank you for your work on this important issue. It looks awesome so far!
I have a few ideas and suggestions that could be interesting:
It's helpful to think about Calypso not as one big web app, but as a conglomerate of many small apps that are fairly indepenent from each other. There is signup flow for new users and sites at /start
, Reader app at /
, plugins management at /plugins
, domain management at /domains
and many others. A full list of these "sections" is in client/sections.js
. That file is transformed by Webpack loader at server/bundler/loader.js
into code that async loads the code for that section and executes it.
The major current weakness of that system is that it async loads mostly just React UI components for a particular section. The data part -- the reducers and data-layer effect handlers -- is still a part of the global build.js
chunk. If I navigate to root path of Calypso, I use only the Reader app, but the state tree contains everything, including extensions:
I think the most promising approach for your task is to find reducers that are used only by one particular section. I.e., they are specific to a single Calypso sub-app and don't implement any cross-section functionality. There should be plenty of them. Then load these reducers asynchronously in the section-loading code generated by the server/bundler/loader
loader.
Extensions should be the easiest target for this. They are explicitly designed to be independent mini-apps that are as independent from the rest of Calypso as possible. You could start with the wp-job-manager
extension for example. Note that every extension has a "manifest" file called package.json
and that file is loaded in sections.js
. Try to load its reducer asynchronously. I don't want to see extensions.wpJobManager
in my state tree until I actually go to the /extensions/wp-job-manager
route.
The point is that we might not need a generic solution that works with any import
statement at any place. Maybe everything we'll ever need is being able to async load state pieces at a "section" level. Adding that functionality only to the section-loading code might be much easier and more maintainable.
@jsnajdr Thanks for the comment!
I spent some time looking into how extensions are being loaded; it does look like an easier target — assuming nowhere else in Calypso access the state of a specific extension. The other thing is that it's data-layer and selectors have already been moved into its section chunk. data-layers are loaded dynamically with state/data-layer/extensions-middleware.js
and selectors only gets imported by the extension components. It would be pretty straightforward for me to complete an "extension reducer loader" in the bundler. I will share my work when it's ready.
I would still love to investigate where it leads though, I mean, whether or not this can become a generic solution to other sections. But I will aim to achieve the small win first.
Thank you again!
I once wrote some code to dynamically add and remove reducers for extensions. Several felt that Calypso wasn't ready for it at the time, but perhaps that is changing now?
One question we have to ask ourselves about things like this is, if we can dynamically add/load things, can we also dynamically remove them as well? We can't really dynamically unload code, as far as I know, but there could be performance gains to removing things like reducers when we're not using them.
Also, specifically for reducers and redux state, should we consider creating a separate redux state tree for each extension? That would make things like removing/unloading easier.
Allow me to summarize where I am going currently: so with #20542 there is going to be a generalized, special combineReducersAndAddLater()
method (obviously naming can be improved) that accepts adding more reducer function after-the-fact. This will be much useful than managing extension reducers. With that, by only import the reducer at the places where it will be eventually utilized (i.e. action creators and selectors), we can tweak our dependency graph and move away the reducer from the build bundle.
Here is how we could do it, with #20542, again, using timezones
as example:
diff --git a/client/state/index.js b/client/state/index.js
index 5dd130953e..19532228cd 100644
--- a/client/state/index.js
+++ b/client/state/index.js
@@ -12,5 +12,4 @@ import { reducer as form } from 'redux-form';
* Internal dependencies
*/
-import { combineReducers } from 'state/utils';
import { combineReducersAndAddLater, reducerRegistryEnhancer } from './reducer-registry';
import activityLog from './activity-log/reducer';
@@ -74,5 +73,4 @@ import storedCards from './stored-cards/reducer';
import support from './support/reducer';
import terms from './terms/reducer';
-import timezones from './timezones/reducer';
import themes from './themes/reducer';
import ui from './ui/reducer';
@@ -148,5 +146,4 @@ const reducers = {
support,
terms,
- timezones,
themes,
ui,
@@ -158,5 +155,5 @@ const reducers = {
};
-export const reducer = combineReducers( reducers );
+export const reducer = combineReducersAndAddLater( reducers );
/**
diff --git a/client/state/selectors/get-raw-offsets.js b/client/state/selectors/get-raw-offsets.js
index 6aa0193b30..04f83e3957 100644
--- a/client/state/selectors/get-raw-offsets.js
+++ b/client/state/selectors/get-raw-offsets.js
@@ -7,4 +7,12 @@
import { get } from 'lodash';
+/**
+ * Internal dependencies
+ */
+import { addReducers } from 'state/reducer-registry';
+import timezones from 'state/timezones/reducer';
+
+addReducers( { timezones } );
+
/**
* Return manual utc offsets data
diff --git a/client/state/selectors/get-timezones-by-continent.js b/client/state/selectors/get-timezones-by-continent.js
index c141086900..baabe039ce 100644
--- a/client/state/selectors/get-timezones-by-continent.js
+++ b/client/state/selectors/get-timezones-by-continent.js
@@ -7,4 +7,12 @@
import { get } from 'lodash';
+/**
+ * Internal dependencies
+ */
+import { addReducers } from 'state/reducer-registry';
+import timezones from 'state/timezones/reducer';
+
+addReducers( { timezones } );
+
/**
* Return the timezones by continent data
diff --git a/client/state/selectors/get-timezones-labels.js b/client/state/selectors/get-timezones-labels.js
index 66ad78faa9..54bf52e410 100644
--- a/client/state/selectors/get-timezones-labels.js
+++ b/client/state/selectors/get-timezones-labels.js
@@ -7,4 +7,12 @@
import { get } from 'lodash';
+/**
+ * Internal dependencies
+ */
+import { addReducers } from 'state/reducer-registry';
+import timezones from 'state/timezones/reducer';
+
+addReducers( { timezones } );
+
/**
* Return an object of timezones.
diff --git a/client/state/selectors/get-timezones.js b/client/state/selectors/get-timezones.js
index d96bd703d4..03e07ba9f0 100644
--- a/client/state/selectors/get-timezones.js
+++ b/client/state/selectors/get-timezones.js
@@ -11,4 +11,8 @@ import { get, map, toPairs } from 'lodash';
*/
import { getTimezonesLabel } from 'state/selectors';
+import { addReducers } from 'state/reducer-registry';
+import timezones from 'state/timezones/reducer';
+
+addReducers( { timezones } );
/**
diff --git a/client/state/timezones/actions.js b/client/state/timezones/actions.js
index c1e3bd29d7..177f9e427d 100644
--- a/client/state/timezones/actions.js
+++ b/client/state/timezones/actions.js
@@ -6,4 +6,8 @@
import { TIMEZONES_RECEIVE, TIMEZONES_REQUEST } from 'state/action-types';
+import { addReducers } from 'state/reducer-registry';
+import timezones from 'state/timezones/reducer';
+
+addReducers( { timezones } );
export const requestTimezones = () => ( {
Upon checking the effect of the patch above, I realized I am only half way there from making this useful. While dependency of selectors has been fixed in #20231, state/timezones/actions
is still imported by wpcom data-layer and thus the reducer.
It is certainly possible to move some handlers away from start-up. extensions-middleware.js
serves as the example to do that. I will need to look up how the current file structure affects how we merge the handlers and load them asynchronously. This is what I am going to investigate next.
Update: I've set up a development branch to piece everything together.
https://github.com/Automattic/wp-calypso/compare/master...timdream:issue-19829
It currently consists the pull request I've submitted, and the previous code snippet that attempt to move timezones
reducers and data-layer handlers out of the build chunk. Almost every other components are a lot complex than the timezones
, so currently I am working on a codemod, hopefully to move the imports automatically and deterministically.
The development branch updated with WIP work on codemod to move data-layer imports. See also Automattic/calypso-codemods#2.
I've also noticed the fact a few data-layer handlers added back in #18098 has no corresponding action dispatcher/creators. There is essentially dead code currently. I wonder if I should push a quick pull request to not import them for now.
I've decided to change the approach of my script, so that we could have more confidence changing the code based on its result.
The script will scan through the entire client
codebase and try to classify every file into tests, data-layer handlers, action creators/dispatchers, and reducers. With the classification, we will know how to move the imports
around.
The next step here is to reduce the size of the last list, and produce the actual code mode that modify the code for the second and the third list.
The following action types are referenced by one data-layer handler but not any action dispatchers/ceators nor reducers. The data-layer handler is never called:
COMMENTS_REQUEST
COMMENTS_COUNT_RECEIVE
COMMENTS_LIST_REQUEST
COMMENT_REQUEST
COMMENTS_EDIT
COMMENT_COUNTS_REQUEST
COMMENT_COUNTS_UPDATE
COMMENTS_TREE_SITE_REQUEST
COUNTRIES_DOMAINS_FETCH
SIMPLE_PAYMENTS_PRODUCTS_LIST_ADD
SIMPLE_PAYMENTS_PRODUCTS_LIST_EDIT
SIMPLE_PAYMENTS_PRODUCTS_LIST_DELETE
COUNTRIES_SMS_FETCH
CONCIERGE_BOOK_APPOINTMENT
COUNTRIES_PAYMENTS_FETCH
READER_FOLLOWS_SYNC_PAGE
COMMENTS_REPLY_WRITE
COMMENTS_WRITE
The following action types are referenced by one data-layer handler and many action dispatchers/creators. Imports of the data-layer handler should be able to safely move to action creators/dispatchers:
AUTOMATED_TRANSFER_ELIGIBILITY_REQUEST
AUTOMATED_TRANSFER_INITIATE_WITH_PLUGIN_ZIP
SITE_CHECKLIST_REQUEST
SITE_CHECKLIST_TASK_UPDATE
ACTIVITY_LOG_REQUEST
ACTIVITY_LOG_WATCH
REWIND_DEACTIVATE_REQUEST
REWIND_RESTORE_PROGRESS_REQUEST
REWIND_STATUS_REQUEST
REWIND_BACKUP_PROGRESS_REQUEST
JETPACK_ONBOARDING_SETTINGS_SAVE
JITM_DISMISS
AFFILIATE_REFERRAL
MEDIA_ITEM_REQUEST
MEDIA_REQUEST
PRIVACY_POLICY_REQUEST
USER_PROFILE_LINKS_ADD
USER_PROFILE_LINKS_DELETE
USER_PROFILE_LINKS_REQUEST
LOGIN_AUTH_ACCOUNT_TYPE_REQUESTING
LOGIN_AUTH_ACCOUNT_TYPE_REQUEST_FAILURE
TWO_FACTOR_AUTHENTICATION_PUSH_POLL_START
TWO_FACTOR_AUTHENTICATION_PUSH_POLL_STOP
TWO_FACTOR_AUTHENTICATION_UPDATE_NONCE
NOTIFICATION_SETTINGS_REQUEST
HTTP_REQUEST
THEME_FILTERS_REQUEST
USERS_REQUEST
USER_SETTINGS_REQUEST
USER_SETTINGS_SAVE
REWIND_STATE_REQUEST
TIMEZONES_REQUEST
USER_DEVICES_REQUEST
ACCOUNT_RECOVERY_RESET_OPTIONS_REQUEST
ACCOUNT_RECOVERY_RESET_REQUEST
ACCOUNT_RECOVERY_RESET_VALIDATE_REQUEST
ACCOUNT_RECOVERY_RESET_PASSWORD_REQUEST
DIRECTLY_ASK_QUESTION
DIRECTLY_INITIALIZATION_START
EMAIL_VERIFY_REQUEST
GRAVATAR_UPLOAD_REQUEST
JETPACK_CREDENTIALS_AUTOCONFIGURE
JETPACK_CREDENTIALS_DELETE
JETPACK_CREDENTIALS_REQUEST
READER_UNFOLLOW
READER_FOLLOWS_SYNC_START
READER_SUBSCRIBE_TO_NEW_POST_EMAIL
READER_UNSUBSCRIBE_TO_NEW_POST_EMAIL
READER_SUBSCRIBE_TO_NEW_COMMENT_EMAIL
READER_UNSUBSCRIBE_TO_NEW_COMMENT_EMAIL
READER_UPDATE_NEW_POST_EMAIL_SUBSCRIPTION
READER_SITE_BLOCK
READER_SITE_UNBLOCK
PLUGIN_UPLOAD
READER_FEED_SEARCH_REQUEST
READER_RECOMMENDED_SITES_REQUEST
READER_TEAMS_REQUEST
READER_CONVERSATION_FOLLOW
READER_CONVERSATION_MUTE
SITES_BLOG_STICKER_ADD
SITES_BLOG_STICKER_REMOVE
SITES_BLOG_STICKER_LIST
POST_REVISIONS_REQUEST
READER_STREAMS_PAGE_REQUEST
SIMPLE_PAYMENTS_PRODUCT_GET
SIMPLE_PAYMENTS_PRODUCTS_LIST
VIDEO_EDITOR_UPDATE_POSTER
The following action types is only referenced by action dispatchers/creators and reducers. Imports of dispatchers/creators should be able to safely move to reducers:
SIGNUP_COMPLETE_RESET
SIGNUP_DEPENDENCY_STORE_UPDATE
SIGNUP_OPTIONAL_DEPENDENCY_SUGGESTED_USERNAME_SET
BILLING_RECEIPT_EMAIL_SEND
BILLING_TRANSACTIONS_RECEIVE
BILLING_TRANSACTIONS_REQUEST
BILLING_TRANSACTIONS_REQUEST_FAILURE
BILLING_TRANSACTIONS_REQUEST_SUCCESS
SITE_CHECKLIST_RECEIVE
ACCOUNT_RECOVERY_SETTINGS_FETCH
ACCOUNT_RECOVERY_SETTINGS_FETCH_SUCCESS
COUNTRY_STATES_RECEIVE
COUNTRY_STATES_REQUEST
COUNTRY_STATES_REQUEST_FAILURE
COUNTRY_STATES_REQUEST_SUCCESS
GEO_RECEIVE
GEO_REQUEST
GEO_REQUEST_FAILURE
GEO_REQUEST_SUCCESS
CONCIERGE_SIGNUP_FORM_UPDATE
CONCIERGE_UPDATE_BOOKING_STATUS
CURRENT_USER_ID_SET
CURRENT_USER_FLAGS_RECEIVE
DOCUMENT_HEAD_LINK_SET
DOCUMENT_HEAD_META_SET
DOCUMENT_HEAD_TITLE_SET
DOCUMENT_HEAD_UNREAD_COUNT_SET
HAPPINESS_ENGINEERS_FETCH
HAPPINESS_ENGINEERS_RECEIVE
HAPPINESS_ENGINEERS_FETCH_FAILURE
HAPPINESS_ENGINEERS_FETCH_SUCCESS
FOLLOWERS_RECEIVE
FOLLOWERS_REQUEST
FOLLOWERS_REQUEST_ERROR
FOLLOWER_REMOVE_ERROR
FOLLOWER_REMOVE_REQUEST
FOLLOWER_REMOVE_SUCCESS
CONNECTION_LOST
CONNECTION_RESTORED
GOOGLE_APPS_USERS_FETCH
GOOGLE_APPS_USERS_FETCH_COMPLETED
GOOGLE_APPS_USERS_FETCH_FAILED
JETPACK_ONBOARDING_CREDENTIALS_RECEIVE
OAUTH2_CLIENT_DATA_REQUEST_SUCCESS
JETPACK_SYNC_START_REQUEST
JETPACK_SYNC_START_SUCCESS
JETPACK_SYNC_START_ERROR
JETPACK_SYNC_STATUS_REQUEST
JETPACK_SYNC_STATUS_SUCCESS
JETPACK_SYNC_STATUS_ERROR
NPS_SURVEY_SET_ELIGIBILITY
NPS_SURVEY_MARK_SHOWN_THIS_SESSION
NPS_SURVEY_SUBMIT_REQUESTING
NPS_SURVEY_SUBMIT_REQUEST_FAILURE
NPS_SURVEY_SUBMIT_REQUEST_SUCCESS
NPS_SURVEY_SUBMIT_WITH_NO_SCORE_REQUESTING
NPS_SURVEY_SUBMIT_WITH_NO_SCORE_REQUEST_FAILURE
NPS_SURVEY_SUBMIT_WITH_NO_SCORE_REQUEST_SUCCESS
PAGE_TEMPLATES_RECEIVE
PAGE_TEMPLATES_REQUEST
PAGE_TEMPLATES_REQUEST_FAILURE
PAGE_TEMPLATES_REQUEST_SUCCESS
JITM_SET
POST_TYPES_RECEIVE
POST_TYPES_REQUEST
POST_TYPES_REQUEST_SUCCESS
POST_TYPES_REQUEST_FAILURE
POST_FORMATS_RECEIVE
POST_FORMATS_REQUEST
POST_FORMATS_REQUEST_SUCCESS
POST_FORMATS_REQUEST_FAILURE
SHORTCODE_RECEIVE
SHORTCODE_REQUEST
SHORTCODE_REQUEST_FAILURE
SHORTCODE_REQUEST_SUCCESS
PREFERENCES_SET
PREFERENCES_RECEIVE
PREFERENCES_FETCH
PREFERENCES_FETCH_SUCCESS
PREFERENCES_FETCH_FAILURE
PREFERENCES_SAVE_SUCCESS
POST_DELETE
POST_EDIT
POST_REQUEST
POST_REQUEST_SUCCESS
POST_REQUEST_FAILURE
POST_RESTORE
POST_SAVE
POSTS_RECEIVE
POSTS_REQUEST
POSTS_REQUEST_SUCCESS
POSTS_REQUEST_FAILURE
SITE_REQUEST
SITE_REQUEST_SUCCESS
MEDIA_ITEM_REQUEST_FAILURE
MEDIA_ITEM_REQUEST_SUCCESS
MEDIA_ITEM_REQUESTING
MEDIA_RECEIVE
MEDIA_REQUEST_FAILURE
MEDIA_REQUEST_SUCCESS
MEDIA_REQUESTING
PRIVACY_POLICY_ADD
PRODUCTS_LIST_RECEIVE
PRODUCTS_LIST_REQUEST
PRODUCTS_LIST_REQUEST_FAILURE
USER_PROFILE_LINKS_ADD_DUPLICATE
USER_PROFILE_LINKS_ADD_FAILURE
USER_PROFILE_LINKS_ADD_MALFORMED
USER_PROFILE_LINKS_ADD_SUCCESS
USER_PROFILE_LINKS_DELETE_FAILURE
USER_PROFILE_LINKS_DELETE_SUCCESS
USER_PROFILE_LINKS_RECEIVE
USER_PROFILE_LINKS_RESET_ERRORS
PRIVACY_PROTECTION_CANCEL_COMPLETED
PRIVACY_PROTECTION_CANCEL_FAILED
PURCHASES_REMOVE
PURCHASES_SITE_FETCH
PURCHASES_SITE_FETCH_COMPLETED
PURCHASES_SITE_FETCH_FAILED
PURCHASES_USER_FETCH
PURCHASES_USER_FETCH_COMPLETED
PURCHASES_USER_FETCH_FAILED
PURCHASE_REMOVE_FAILED
EDITOR_START
EDITOR_STOP
SITE_ROLES_RECEIVE
SITE_ROLES_REQUEST
SITE_ROLES_REQUEST_FAILURE
SITE_ROLES_REQUEST_SUCCESS
LOGIN_AUTH_ACCOUNT_TYPE_REQUEST
LOGIN_AUTH_ACCOUNT_TYPE_RESET
LOGIN_FORM_UPDATE
LOGIN_REQUEST
LOGIN_REQUEST_FAILURE
LOGIN_REQUEST_SUCCESS
LOGOUT_REQUEST
LOGOUT_REQUEST_FAILURE
LOGOUT_REQUEST_SUCCESS
SOCIAL_LOGIN_REQUEST
SOCIAL_LOGIN_REQUEST_FAILURE
SOCIAL_LOGIN_REQUEST_SUCCESS
SOCIAL_CREATE_ACCOUNT_REQUEST
SOCIAL_CREATE_ACCOUNT_REQUEST_FAILURE
SOCIAL_CREATE_ACCOUNT_REQUEST_SUCCESS
SOCIAL_CONNECT_ACCOUNT_REQUEST
SOCIAL_CONNECT_ACCOUNT_REQUEST_FAILURE
SOCIAL_CONNECT_ACCOUNT_REQUEST_SUCCESS
SOCIAL_DISCONNECT_ACCOUNT_REQUEST
SOCIAL_DISCONNECT_ACCOUNT_REQUEST_FAILURE
SOCIAL_DISCONNECT_ACCOUNT_REQUEST_SUCCESS
TWO_FACTOR_AUTHENTICATION_LOGIN_REQUEST
TWO_FACTOR_AUTHENTICATION_LOGIN_REQUEST_FAILURE
TWO_FACTOR_AUTHENTICATION_LOGIN_REQUEST_SUCCESS
TWO_FACTOR_AUTHENTICATION_SEND_SMS_CODE_REQUEST
TWO_FACTOR_AUTHENTICATION_SEND_SMS_CODE_REQUEST_FAILURE
TWO_FACTOR_AUTHENTICATION_SEND_SMS_CODE_REQUEST_SUCCESS
PUSH_NOTIFICATIONS_API_READY
PUSH_NOTIFICATIONS_AUTHORIZE
PUSH_NOTIFICATIONS_BLOCK
PUSH_NOTIFICATIONS_MUST_PROMPT
PUSH_NOTIFICATIONS_RECEIVE_REGISTER_DEVICE
PUSH_NOTIFICATIONS_RECEIVE_UNREGISTER_DEVICE
PUSH_NOTIFICATIONS_TOGGLE_ENABLED
PUSH_NOTIFICATIONS_TOGGLE_UNBLOCK_INSTRUCTIONS
SITE_SETTINGS_REQUEST
SITE_SETTINGS_REQUEST_FAILURE
SITE_SETTINGS_REQUEST_SUCCESS
SITE_SETTINGS_SAVE_FAILURE
SITE_SETTINGS_UPDATE
SITE_DELETE_SUCCESS
SITES_REQUEST
SITES_REQUEST_SUCCESS
SITES_REQUEST_FAILURE
RECEIPT_FETCH
RECEIPT_FETCH_COMPLETED
RECEIPT_FETCH_FAILED
USER_RECEIVE
THEME_ACTIVATE_SUCCESS
WORDADS_SITE_APPROVE_REQUEST_SUCCESS
NOTICE_CREATE
NOTICE_REMOVE
STORED_CARDS_ADD_COMPLETED
STORED_CARDS_DELETE
STORED_CARDS_DELETE_COMPLETED
STORED_CARDS_DELETE_FAILED
STORED_CARDS_FETCH
STORED_CARDS_FETCH_COMPLETED
STORED_CARDS_FETCH_FAILED
SUPPORT_USER_ACTIVATE
SUPPORT_USER_ERROR
SUPPORT_USER_PREFILL
SUPPORT_USER_SET_USERNAME
SUPPORT_USER_TOKEN_FETCH
SUPPORT_USER_TOGGLE_DIALOG
ACTIVE_THEME_REQUEST
ACTIVE_THEME_REQUEST_SUCCESS
ACTIVE_THEME_REQUEST_FAILURE
THEME_ACTIVATE
THEME_CLEAR_ACTIVATED
THEME_INSTALL
THEME_INSTALL_SUCCESS
THEME_INSTALL_FAILURE
THEME_REQUEST
THEME_REQUEST_SUCCESS
THEME_REQUEST_FAILURE
THEMES_REQUEST
THEMES_REQUEST_FAILURE
THEME_PREVIEW_OPTIONS
THEME_PREVIEW_STATE
TERM_REMOVE
TERMS_RECEIVE
TERMS_REQUEST
TERMS_REQUEST_SUCCESS
TERMS_REQUEST_FAILURE
THEME_BACK_PATH_SET
THEME_TRANSFER_INITIATE_PROGRESS
THEME_TRANSFER_STATUS_FAILURE
THEME_UPLOAD_START
THEME_UPLOAD_SUCCESS
THEME_UPLOAD_FAILURE
THEME_UPLOAD_CLEAR
THEME_UPLOAD_PROGRESS
USER_SETTINGS_UPDATE
USER_SETTINGS_UNSAVED_CLEAR
USER_SETTINGS_UNSAVED_SET
USER_SETTINGS_UNSAVED_REMOVE
TIMEZONES_RECEIVE
USER_DEVICES_ADD
ACCOUNT_RECOVERY_RESET_OPTIONS_RECEIVE
ACCOUNT_RECOVERY_RESET_OPTIONS_ERROR
ACCOUNT_RECOVERY_RESET_UPDATE_USER_DATA
ACCOUNT_RECOVERY_RESET_VALIDATE_REQUEST_SUCCESS
ACCOUNT_RECOVERY_RESET_VALIDATE_REQUEST_ERROR
ACCOUNT_RECOVERY_RESET_SET_METHOD
ACCOUNT_RECOVERY_RESET_SET_VALIDATION_KEY
ACCOUNT_RECOVERY_SETTINGS_UPDATE
ACCOUNT_RECOVERY_SETTINGS_DELETE
ACCOUNT_RECOVERY_SETTINGS_RESEND_VALIDATION
ACCOUNT_RECOVERY_SETTINGS_VALIDATE_PHONE
HELP_COURSES_RECEIVE
DIRECTLY_INITIALIZATION_SUCCESS
DIRECTLY_INITIALIZATION_ERROR
EMAIL_VERIFY_STATE_RESET
HAPPYCHAT_IO_RECEIVE_ACCEPT
HAPPYCHAT_IO_RECEIVE_DISCONNECT
HAPPYCHAT_IO_RECEIVE_INIT
HAPPYCHAT_IO_RECEIVE_RECONNECTING
HAPPYCHAT_IO_RECEIVE_STATUS
HAPPYCHAT_IO_REQUEST_TRANSCRIPT_RECEIVE
HAPPYCHAT_IO_REQUEST_TRANSCRIPT_TIMEOUT
DOMAIN_MANAGEMENT_CONTACT_DETAILS_CACHE_RECEIVE
DOMAIN_MANAGEMENT_CONTACT_DETAILS_CACHE_REQUEST
DOMAIN_MANAGEMENT_CONTACT_DETAILS_CACHE_REQUEST_FAILURE
DOMAIN_MANAGEMENT_CONTACT_DETAILS_CACHE_REQUEST_SUCCESS
DOMAIN_MANAGEMENT_CONTACT_DETAILS_CACHE_UPDATE
DOMAIN_MANAGEMENT_WHOIS_RECEIVE
DOMAIN_MANAGEMENT_WHOIS_REQUEST
DOMAIN_MANAGEMENT_WHOIS_REQUEST_FAILURE
DOMAIN_MANAGEMENT_WHOIS_REQUEST_SUCCESS
DOMAIN_MANAGEMENT_WHOIS_SAVE
DOMAIN_MANAGEMENT_WHOIS_SAVE_FAILURE
DOMAIN_MANAGEMENT_WHOIS_SAVE_SUCCESS
DOMAIN_MANAGEMENT_WHOIS_UPDATE
HELP_TICKET_CONFIGURATION_REQUEST
HELP_TICKET_CONFIGURATION_REQUEST_SUCCESS
HELP_TICKET_CONFIGURATION_REQUEST_FAILURE
HELP_TICKET_CONFIGURATION_DISMISS_ERROR
DOMAINS_SUGGESTIONS_RECEIVE
DOMAINS_SUGGESTIONS_REQUEST
DOMAINS_SUGGESTIONS_REQUEST_FAILURE
DOMAINS_SUGGESTIONS_REQUEST_SUCCESS
HAPPYCHAT_OPEN
HAPPYCHAT_MINIMIZING
HAPPYCHAT_SET_CURRENT_MESSAGE
READER_LIST_DISMISS_NOTICE
READER_LIST_REQUEST
READER_LIST_REQUEST_SUCCESS
READER_LIST_REQUEST_FAILURE
READER_LIST_UPDATE_SUCCESS
READER_LIST_UPDATE_FAILURE
READER_LIST_UPDATE_TITLE
READER_LIST_UPDATE_DESCRIPTION
READER_LISTS_RECEIVE
READER_LISTS_REQUEST
READER_LISTS_REQUEST_SUCCESS
READER_LISTS_REQUEST_FAILURE
READER_LISTS_UNFOLLOW_SUCCESS
PLUGINS_RECEIVE
PLUGINS_REQUEST
PLUGINS_REQUEST_SUCCESS
PLUGINS_REQUEST_FAILURE
READER_FOLLOW_ERROR
READER_RECORD_FOLLOW
READER_RECORD_UNFOLLOW
READER_FOLLOWS_RECEIVE
READER_FOLLOWS_SYNC_COMPLETE
PLUGIN_SETUP_INSTRUCTIONS_FETCH
PLUGIN_SETUP_INSTRUCTIONS_RECEIVE
PLUGIN_SETUP_INSTALL
PLUGIN_SETUP_CONFIGURE
PLUGIN_SETUP_FINISH
PLUGIN_SETUP_ERROR
READER_POSTS_RECEIVE
READER_FEED_REQUEST
READER_FEED_REQUEST_SUCCESS
READER_FEED_REQUEST_FAILURE
READER_FEED_UPDATE
WPORG_PLUGIN_DATA_RECEIVE
FETCH_WPORG_PLUGIN_DATA
READER_FEED_SEARCH_RECEIVE
READER_SITE_REQUEST
READER_SITE_REQUEST_SUCCESS
READER_SITE_REQUEST_FAILURE
READER_SITE_UPDATE
PLUGIN_UPLOAD_CLEAR
PLUGIN_UPLOAD_COMPLETE
PLUGIN_UPLOAD_ERROR
PLUGIN_UPLOAD_PROGRESS
MAGIC_LOGIN_HIDE_REQUEST_FORM
MAGIC_LOGIN_HIDE_REQUEST_NOTICE
MAGIC_LOGIN_REQUEST_AUTH_ERROR
MAGIC_LOGIN_REQUEST_AUTH_FETCH
MAGIC_LOGIN_REQUEST_AUTH_SUCCESS
MAGIC_LOGIN_REQUEST_LOGIN_EMAIL_ERROR
MAGIC_LOGIN_REQUEST_LOGIN_EMAIL_FETCH
MAGIC_LOGIN_REQUEST_LOGIN_EMAIL_SUCCESS
MAGIC_LOGIN_RESET_REQUEST_FORM
MAGIC_LOGIN_SHOW_LINK_EXPIRED
MAGIC_LOGIN_SHOW_CHECK_YOUR_EMAIL_PAGE
READER_RECOMMENDED_SITES_RECEIVE
READER_RELATED_POSTS_REQUEST
READER_RELATED_POSTS_REQUEST_SUCCESS
READER_RELATED_POSTS_REQUEST_FAILURE
READER_RELATED_POSTS_RECEIVE
READER_THUMBNAIL_REQUEST
READER_THUMBNAIL_REQUEST_SUCCESS
READER_THUMBNAIL_REQUEST_FAILURE
READER_THUMBNAIL_RECEIVE
POST_TYPES_TAXONOMIES_RECEIVE
POST_TYPES_TAXONOMIES_REQUEST
POST_TYPES_TAXONOMIES_REQUEST_FAILURE
POST_TYPES_TAXONOMIES_REQUEST_SUCCESS
KEYRING_CONNECTIONS_RECEIVE
KEYRING_CONNECTIONS_REQUEST
KEYRING_CONNECTIONS_REQUEST_FAILURE
KEYRING_CONNECTIONS_REQUEST_SUCCESS
READER_CONVERSATION_UPDATE_FOLLOW_STATUS
SITES_BLOG_STICKER_LIST_RECEIVE
PUBLICIZE_CONNECTION_RECEIVE
PUBLICIZE_CONNECTION_REQUEST
PUBLICIZE_CONNECTION_REQUEST_FAILURE
PUBLICIZE_CONNECTION_REQUEST_SUCCESS
PUBLICIZE_CONNECTIONS_RECEIVE
PUBLICIZE_CONNECTIONS_REQUEST
PUBLICIZE_CONNECTIONS_REQUEST_FAILURE
PUBLICIZE_SHARE
PUBLICIZE_SHARE_SUCCESS
PUBLICIZE_SHARE_FAILURE
PUBLICIZE_SHARE_DISMISS
SHARING_BUTTONS_RECEIVE
SHARING_BUTTONS_REQUEST
SHARING_BUTTONS_REQUEST_FAILURE
SHARING_BUTTONS_REQUEST_SUCCESS
SHARING_BUTTONS_SAVE
SHARING_BUTTONS_SAVE_FAILURE
SHARING_BUTTONS_SAVE_SUCCESS
SHARING_BUTTONS_UPDATE
KEYRING_SERVICES_RECEIVE
KEYRING_SERVICES_REQUEST
KEYRING_SERVICES_REQUEST_FAILURE
KEYRING_SERVICES_REQUEST_SUCCESS
POST_COUNTS_RECEIVE
POST_COUNTS_REQUEST
POST_COUNTS_REQUEST_SUCCESS
POST_COUNTS_REQUEST_FAILURE
EXPORT_ADVANCED_SETTINGS_FETCH
EXPORT_ADVANCED_SETTINGS_FETCH_FAIL
EXPORT_ADVANCED_SETTINGS_RECEIVE
EXPORT_CLEAR
EXPORT_START_REQUEST
EXPORT_POST_TYPE_SET
EXPORT_POST_TYPE_FIELD_SET
POST_LIKES_RECEIVE
POST_LIKES_REQUEST
POST_LIKES_REQUEST_SUCCESS
POST_LIKES_REQUEST_FAILURE
SITE_MEDIA_STORAGE_RECEIVE
SITE_MEDIA_STORAGE_REQUEST
SITE_MEDIA_STORAGE_REQUEST_SUCCESS
SITE_MEDIA_STORAGE_REQUEST_FAILURE
SITE_CONNECTION_STATUS_RECEIVE
SITE_CONNECTION_STATUS_REQUEST
SITE_CONNECTION_STATUS_REQUEST_FAILURE
SITE_CONNECTION_STATUS_REQUEST_SUCCESS
SITE_DOMAINS_RECEIVE
SITE_DOMAINS_REQUEST
SITE_DOMAINS_REQUEST_SUCCESS
SITE_DOMAINS_REQUEST_FAILURE
POST_REVISIONS_RECEIVE
POST_REVISIONS_REQUEST_FAILURE
POST_REVISIONS_REQUEST_SUCCESS
POST_REVISIONS_SELECT
POST_REVISIONS_DIALOG_CLOSE
POST_REVISIONS_DIALOG_OPEN
GUIDED_TRANSFER_HOST_DETAILS_SAVE
GUIDED_TRANSFER_HOST_DETAILS_SAVE_FAILURE
GUIDED_TRANSFER_STATUS_RECEIVE
GUIDED_TRANSFER_STATUS_REQUEST
GUIDED_TRANSFER_STATUS_REQUEST_FAILURE
GUIDED_TRANSFER_STATUS_REQUEST_SUCCESS
SITE_MONITOR_SETTINGS_RECEIVE
SITE_MONITOR_SETTINGS_REQUEST
SITE_MONITOR_SETTINGS_REQUEST_FAILURE
SITE_MONITOR_SETTINGS_REQUEST_SUCCESS
SITE_MONITOR_SETTINGS_UPDATE
SITE_UPDATES_RECEIVE
SITE_UPDATES_REQUEST
SITE_UPDATES_REQUEST_SUCCESS
SITE_UPDATES_REQUEST_FAILURE
SITE_WORDPRESS_UPDATE_REQUEST_SUCCESS
SITE_WORDPRESS_UPDATE_REQUEST_FAILURE
SITE_PLUGIN_UPDATED
SITE_VOUCHERS_ASSIGN_RECEIVE
SITE_VOUCHERS_ASSIGN_REQUEST
SITE_VOUCHERS_ASSIGN_REQUEST_SUCCESS
SITE_VOUCHERS_ASSIGN_REQUEST_FAILURE
SITE_VOUCHERS_RECEIVE
SITE_VOUCHERS_REQUEST
SITE_VOUCHERS_REQUEST_SUCCESS
SITE_VOUCHERS_REQUEST_FAILURE
SITE_STATS_RECEIVE
SITE_STATS_REQUEST
SITE_STATS_REQUEST_FAILURE
SITE_STATS_REQUEST_SUCCESS
POST_STATS_RECEIVE
POST_STATS_REQUEST
POST_STATS_REQUEST_FAILURE
POST_STATS_REQUEST_SUCCESS
READER_STREAMS_PAGE_RECEIVE
READER_STREAMS_SELECT_ITEM
SIMPLE_PAYMENTS_PRODUCT_RECEIVE
SIMPLE_PAYMENTS_PRODUCTS_LIST_RECEIVE
SIMPLE_PAYMENTS_PRODUCTS_LIST_RECEIVE_UPDATE
SIMPLE_PAYMENTS_PRODUCTS_LIST_RECEIVE_DELETE
PREVIEW_SITE_SET
PREVIEW_URL_CLEAR
PREVIEW_URL_SET
PREVIEW_TOOL_SET
PREVIEW_TYPE_SET
PREVIEW_TYPE_RESET
LOCALE_SET
MEDIA_MODAL_VIEW_SET
NPS_SURVEY_DIALOG_IS_SHOWING
OLARK_READY
OLARK_REQUEST
OLARK_TIMEOUT
OLARK_OPERATORS_AVAILABLE
OLARK_OPERATORS_AWAY
OLARK_SET_AVAILABILITY
COMMENTS_QUERY_UPDATE
DROPZONE_SHOW
DROPZONE_HIDE
USER_SUGGESTIONS_RECEIVE
USER_SUGGESTIONS_REQUEST
USER_SUGGESTIONS_REQUEST_SUCCESS
USER_SUGGESTIONS_REQUEST_FAILURE
WORDADS_SITE_APPROVE_REQUEST
WORDADS_SITE_APPROVE_REQUEST_FAILURE
WORDADS_SITE_APPROVE_REQUEST_DISMISS_ERROR
WORDADS_SITE_APPROVE_REQUEST_DISMISS_SUCCESS
WORDADS_STATUS_REQUEST
WORDADS_STATUS_REQUEST_SUCCESS
WORDADS_STATUS_REQUEST_FAILURE
SIGNUP_STEPS_SITE_TITLE_SET
SIGNUP_STEPS_USER_EXPERIENCE_SET
SIGNUP_STEPS_SITE_GOALS_SET
READER_TAG_IMAGES_REQUEST
READER_TAG_IMAGES_REQUEST_SUCCESS
READER_TAG_IMAGES_REQUEST_FAILURE
READER_TAG_IMAGES_RECEIVE
SIGNUP_STEPS_SURVEY_SET
PUBLICIZE_SHARE_ACTIONS_SCHEDULED_REQUEST
PUBLICIZE_SHARE_ACTIONS_SCHEDULED_REQUEST_SUCCESS
PUBLICIZE_SHARE_ACTIONS_SCHEDULED_REQUEST_FAILURE
PUBLICIZE_SHARE_ACTIONS_PUBLISHED_REQUEST
PUBLICIZE_SHARE_ACTIONS_PUBLISHED_REQUEST_SUCCESS
PUBLICIZE_SHARE_ACTIONS_PUBLISHED_REQUEST_FAILURE
PUBLICIZE_SHARE_ACTION_DELETE
PUBLICIZE_SHARE_ACTION_DELETE_SUCCESS
PUBLICIZE_SHARE_ACTION_DELETE_FAILURE
PUBLICIZE_SHARE_ACTION_EDIT
PUBLICIZE_SHARE_ACTION_EDIT_SUCCESS
PUBLICIZE_SHARE_ACTION_EDIT_FAILURE
PUBLICIZE_SHARE_ACTION_SCHEDULE
PUBLICIZE_SHARE_ACTION_SCHEDULE_SUCCESS
PUBLICIZE_SHARE_ACTION_SCHEDULE_FAILURE
SIGNUP_STEPS_DESIGN_TYPE_SET
READER_EXPAND_CARD
READER_RESET_CARD_EXPANSIONS
READER_SIDEBAR_LISTS_TOGGLE
READER_SIDEBAR_TAGS_TOGGLE
EDITOR_LAST_DRAFT_SET
VIDEO_EDITOR_SET_POSTER_URL
VIDEO_EDITOR_SHOW_ERROR
VIDEO_EDITOR_SHOW_UPLOAD_PROGRESS
IMAGE_EDITOR_CROP
IMAGE_EDITOR_COMPUTED_CROP
IMAGE_EDITOR_ROTATE_COUNTERCLOCKWISE
IMAGE_EDITOR_FLIP
IMAGE_EDITOR_SET_ASPECT_RATIO
IMAGE_EDITOR_SET_DEFAULT_ASPECT_RATIO
IMAGE_EDITOR_SET_CROP_BOUNDS
IMAGE_EDITOR_SET_FILE_INFO
IMAGE_EDITOR_STATE_RESET
IMAGE_EDITOR_STATE_RESET_ALL
IMAGE_EDITOR_IMAGE_HAS_LOADED
The following action types has not been classified definitively to be safely modified by codemod:
SERIALIZE
DESERIALIZE
JETPACK_CONNECT_QUERY_SET
SITES_ONCE_CHANGED
COMMENTS_RECEIVE
ANALYTICS_SUPER_PROPS_UPDATE
IMPORTS_AUTHORS_SET_MAPPING
IMPORTS_AUTHORS_START_MAPPING
IMPORTS_FETCH
IMPORTS_FETCH_FAILED
IMPORTS_FETCH_COMPLETED
IMPORTS_IMPORT_CANCEL
IMPORTS_IMPORT_LOCK
IMPORTS_IMPORT_RECEIVE
IMPORTS_IMPORT_RESET
IMPORTS_IMPORT_START
IMPORTS_IMPORT_UNLOCK
IMPORTS_START_IMPORTING
IMPORTS_UPLOAD_FAILED
IMPORTS_UPLOAD_COMPLETED
IMPORTS_UPLOAD_SET_PROGRESS
IMPORTS_UPLOAD_START
IMPORTS_STORE_RESET
AUTOMATED_TRANSFER_ELIGIBILITY_UPDATE
AUTOMATED_TRANSFER_STATUS_SET
AUTOMATED_TRANSFER_STATUS_REQUEST
AUTOMATED_TRANSFER_STATUS_REQUEST_FAILURE
THEME_TRANSFER_INITIATE_REQUEST
THEME_TRANSFER_INITIATE_FAILURE
THEME_TRANSFER_STATUS_RECEIVE
BILLING_RECEIPT_EMAIL_SEND_FAILURE
BILLING_RECEIPT_EMAIL_SEND_SUCCESS
ANALYTICS_EVENT_RECORD
ANALYTICS_MULTI_TRACK
ANALYTICS_PAGE_VIEW_RECORD
ANALYTICS_STAT_BUMP
ANALYTICS_TRACKING_ON
ANALYTICS_TRACKS_ANONID_SET
ACCOUNT_RECOVERY_SETTINGS_FETCH_FAILED
CONCIERGE_AVAILABLE_TIMES_REQUEST
CONCIERGE_AVAILABLE_TIMES_UPDATE
CONCIERGE_APPOINTMENT_CREATE
*
HELP_CONTACT_FORM_SITE_SELECT
COUNTRIES_DOMAINS_UPDATED
COUNTRIES_PAYMENTS_UPDATED
COUNTRIES_SMS_UPDATED
HAPPYCHAT_BLUR
HAPPYCHAT_FOCUS
HAPPYCHAT_IO_INIT
HAPPYCHAT_IO_REQUEST_TRANSCRIPT
HAPPYCHAT_IO_SEND_MESSAGE_EVENT
HAPPYCHAT_IO_SEND_MESSAGE_LOG
HAPPYCHAT_IO_SEND_MESSAGE_MESSAGE
HAPPYCHAT_IO_SEND_MESSAGE_USERINFO
HAPPYCHAT_IO_SEND_PREFERENCES
HAPPYCHAT_IO_SEND_TYPING
SITE_RECEIVE
SITE_PLANS_FETCH_COMPLETED
SITES_RECEIVE
PLANS_RECEIVE
ROUTE_SET
COMMENTS_CHANGE_STATUS
EXPORT_COMPLETE
EXPORT_FAILURE
EXPORT_STARTED
JETPACK_CONNECT_AUTHORIZE
MEDIA_DELETE
PLUGIN_ACTIVATE_REQUEST
PLUGIN_SETUP_ACTIVATE
POST_SAVE_SUCCESS
PUBLICIZE_CONNECTION_CREATE
PUBLICIZE_CONNECTION_DELETE
PURCHASE_REMOVE_COMPLETED
SITE_SETTINGS_SAVE_SUCCESS
ACTIVITY_LOG_UPDATE
REWIND_ACTIVATE_FAILURE
REWIND_ACTIVATE_REQUEST
REWIND_ACTIVATE_SUCCESS
REWIND_DEACTIVATE_FAILURE
REWIND_DEACTIVATE_SUCCESS
REWIND_RESTORE
REWIND_RESTORE_DISMISS
REWIND_RESTORE_DISMISS_PROGRESS
REWIND_RESTORE_REQUEST
REWIND_RESTORE_UPDATE_ERROR
REWIND_RESTORE_UPDATE_PROGRESS
REWIND_STATUS_ERROR
REWIND_STATUS_UPDATE
REWIND_BACKUP
REWIND_BACKUP_REQUEST
REWIND_BACKUP_DISMISS
REWIND_BACKUP_UPDATE_ERROR
REWIND_BACKUP_UPDATE_PROGRESS
REWIND_BACKUP_DISMISS_PROGRESS
HAPPYCHAT_IO_RECEIVE_MESSAGE
OAUTH2_CLIENT_DATA_REQUEST
OAUTH2_CLIENT_DATA_REQUEST_FAILURE
DESERIALIZE_PART
PLANS_REQUEST
PLANS_REQUEST_SUCCESS
PLANS_REQUEST_FAILURE
PREFERENCES_SAVE
PREFERENCES_SAVE_FAILURE
POST_DELETE_SUCCESS
POST_DELETE_FAILURE
POST_RESTORE_FAILURE
POST_RESTORE_SUCCESS
POST_SAVE_FAILURE
JETPACK_CONNECT_CHECK_URL
JETPACK_CONNECT_CHECK_URL_RECEIVE
JETPACK_CONNECT_CONFIRM_JETPACK_STATUS
JETPACK_CONNECT_COMPLETE_FLOW
JETPACK_CONNECT_DISMISS_URL_STATUS
JETPACK_CONNECT_AUTHORIZE_LOGIN_COMPLETE
JETPACK_CONNECT_AUTHORIZE_RECEIVE
JETPACK_CONNECT_AUTHORIZE_RECEIVE_SITE_LIST
JETPACK_CONNECT_CREATE_ACCOUNT
JETPACK_CONNECT_CREATE_ACCOUNT_RECEIVE
JETPACK_CONNECT_REDIRECT_WP_ADMIN
JETPACK_CONNECT_REDIRECT_XMLRPC_ERROR_FALLBACK_URL
JETPACK_CONNECT_RETRY_AUTH
JETPACK_CONNECT_SSO_AUTHORIZE_REQUEST
JETPACK_CONNECT_SSO_AUTHORIZE_SUCCESS
JETPACK_CONNECT_SSO_AUTHORIZE_ERROR
JETPACK_CONNECT_SSO_VALIDATION_REQUEST
JETPACK_CONNECT_SSO_VALIDATION_SUCCESS
JETPACK_CONNECT_SSO_VALIDATION_ERROR
JETPACK_CONNECT_USER_ALREADY_CONNECTED
SITE_REQUEST_FAILURE
PREVIEW_MARKUP_RECEIVE
PREVIEW_CUSTOMIZATIONS_CLEAR
PREVIEW_CUSTOMIZATIONS_UPDATE
PREVIEW_CUSTOMIZATIONS_UNDO
PREVIEW_CUSTOMIZATIONS_SAVED
JETPACK_DISCONNECT_RECEIVE
NOTIFICATIONS_PANEL_TOGGLE
SELECTED_SITE_SET
SITE_DELETE_RECEIVE
SELECTED_SITE_SUBSCRIBE
SELECTED_SITE_UNSUBSCRIBE
PRIVACY_PROTECTION_CANCEL
SITE_SETTINGS_RECEIVE
SITE_SETTINGS_SAVE
SITE_DELETE
SITE_DELETE_FAILURE
PUSH_NOTIFICATIONS_API_NOT_READY
LOGIN_AUTH_ACCOUNT_TYPE_REQUEST_SUCCESS
TWO_FACTOR_AUTHENTICATION_PUSH_POLL_COMPLETED
NOTIFICATION_SETTINGS_UPDATE
THEME_ACTIVATE_FAILURE
THEME_DELETE_SUCCESS
THEME_FILTERS_ADD
THEMES_REQUEST_SUCCESS
ACCOUNT_RECOVERY_SETTINGS_UPDATE_SUCCESS
ACCOUNT_RECOVERY_SETTINGS_UPDATE_FAILED
ACCOUNT_RECOVERY_SETTINGS_DELETE_SUCCESS
ACCOUNT_RECOVERY_SETTINGS_DELETE_FAILED
ACCOUNT_RECOVERY_SETTINGS_RESEND_VALIDATION_SUCCESS
ACCOUNT_RECOVERY_SETTINGS_RESEND_VALIDATION_FAILED
ACCOUNT_RECOVERY_SETTINGS_VALIDATE_PHONE_SUCCESS
ACCOUNT_RECOVERY_SETTINGS_VALIDATE_PHONE_FAILED
GRAVATAR_RECEIVE_IMAGE_FAILURE
GRAVATAR_UPLOAD_REQUEST_FAILURE
GRAVATAR_UPLOAD_REQUEST_SUCCESS
GUIDED_TRANSFER_HOST_DETAILS_SAVE_SUCCESS
JETPACK_MODULE_ACTIVATE_SUCCESS
JETPACK_MODULE_DEACTIVATE_SUCCESS
JETPACK_MODULE_ACTIVATE_FAILURE
JETPACK_MODULE_DEACTIVATE_FAILURE
KEYRING_CONNECTION_DELETE
KEYRING_CONNECTION_DELETE_FAILURE
PUBLICIZE_CONNECTION_CREATE_FAILURE
PUBLICIZE_CONNECTION_DELETE_FAILURE
PUBLICIZE_CONNECTION_UPDATE
PUBLICIZE_CONNECTION_UPDATE_FAILURE
SITE_MONITOR_SETTINGS_UPDATE_SUCCESS
SITE_MONITOR_SETTINGS_UPDATE_FAILURE
THEME_DELETE_FAILURE
NAVIGATE
THEME_DELETE
THEME_TRANSFER_INITIATE_SUCCESS
REWIND_STATE_UPDATE
SECTION_SET
PREVIEW_IS_SHOWING
WPCOM_HTTP_REQUEST
COMPONENTS_USAGE_STATS_REQUEST
COMPONENTS_USAGE_STATS_RECEIVE
ACCOUNT_RECOVERY_RESET_REQUEST_SUCCESS
ACCOUNT_RECOVERY_RESET_REQUEST_ERROR
ACCOUNT_RECOVERY_RESET_PASSWORD_REQUEST_SUCCESS
ACCOUNT_RECOVERY_RESET_PASSWORD_REQUEST_ERROR
COMMENTS_DELETE
COMMENTS_TREE_SITE_ADD
EMAIL_VERIFY_REQUEST_SUCCESS
EMAIL_VERIFY_REQUEST_FAILURE
HAPPYCHAT_IO_RECEIVE_CONNECT
HAPPYCHAT_IO_RECEIVE_ERROR
HAPPYCHAT_IO_RECEIVE_TOKEN
HAPPYCHAT_IO_RECEIVE_UNAUTHORIZED
GRAVATAR_UPLOAD_RECEIVE
JETPACK_CREDENTIALS_UPDATE
JETPACK_CONNECTION_STATUS_RECEIVE
JETPACK_CONNECTION_STATUS_REQUEST
JETPACK_CONNECTION_STATUS_REQUEST_SUCCESS
JETPACK_CONNECTION_STATUS_REQUEST_FAILURE
JETPACK_DISCONNECT_REQUEST
JETPACK_DISCONNECT_REQUEST_FAILURE
JETPACK_DISCONNECT_REQUEST_SUCCESS
JETPACK_USER_CONNECTION_DATA_RECEIVE
JETPACK_USER_CONNECTION_DATA_REQUEST
JETPACK_USER_CONNECTION_DATA_REQUEST_SUCCESS
JETPACK_USER_CONNECTION_DATA_REQUEST_FAILURE
JETPACK_CREDENTIALS_STORE
JETPACK_CREDENTIALS_UPDATE_SUCCESS
JETPACK_CREDENTIALS_UPDATE_FAILURE
JETPACK_JUMPSTART_ACTIVATE
JETPACK_JUMPSTART_ACTIVATE_SUCCESS
JETPACK_JUMPSTART_ACTIVATE_FAILURE
JETPACK_JUMPSTART_DEACTIVATE
JETPACK_JUMPSTART_DEACTIVATE_SUCCESS
JETPACK_JUMPSTART_DEACTIVATE_FAILURE
JETPACK_JUMPSTART_STATUS_RECEIVE
JETPACK_JUMPSTART_STATUS_REQUEST
JETPACK_JUMPSTART_STATUS_REQUEST_SUCCESS
JETPACK_JUMPSTART_STATUS_REQUEST_FAILURE
JETPACK_MODULE_ACTIVATE
JETPACK_MODULE_DEACTIVATE
JETPACK_MODULES_RECEIVE
JETPACK_MODULES_REQUEST
JETPACK_MODULES_REQUEST_FAILURE
JETPACK_MODULES_REQUEST_SUCCESS
JETPACK_SETTINGS_RECEIVE
JETPACK_SETTINGS_UPDATE_SUCCESS
JETPACK_SETTINGS_REGENERATE_POST_BY_EMAIL
JETPACK_SETTINGS_REGENERATE_POST_BY_EMAIL_SUCCESS
JETPACK_SETTINGS_REGENERATE_POST_BY_EMAIL_FAILURE
JETPACK_SETTINGS_REQUEST
JETPACK_SETTINGS_REQUEST_FAILURE
JETPACK_SETTINGS_REQUEST_SUCCESS
JETPACK_SETTINGS_UPDATE
JETPACK_SETTINGS_UPDATE_FAILURE
PLUGIN_ACTIVATE_REQUEST_SUCCESS
PLUGIN_ACTIVATE_REQUEST_FAILURE
PLUGIN_DEACTIVATE_REQUEST
PLUGIN_DEACTIVATE_REQUEST_SUCCESS
PLUGIN_DEACTIVATE_REQUEST_FAILURE
PLUGIN_UPDATE_REQUEST
PLUGIN_UPDATE_REQUEST_SUCCESS
PLUGIN_UPDATE_REQUEST_FAILURE
PLUGIN_AUTOUPDATE_ENABLE_REQUEST
PLUGIN_AUTOUPDATE_ENABLE_REQUEST_SUCCESS
PLUGIN_AUTOUPDATE_ENABLE_REQUEST_FAILURE
PLUGIN_AUTOUPDATE_DISABLE_REQUEST
PLUGIN_AUTOUPDATE_DISABLE_REQUEST_SUCCESS
PLUGIN_AUTOUPDATE_DISABLE_REQUEST_FAILURE
PLUGIN_INSTALL_REQUEST
PLUGIN_INSTALL_REQUEST_SUCCESS
PLUGIN_INSTALL_REQUEST_FAILURE
PLUGIN_REMOVE_REQUEST
PLUGIN_REMOVE_REQUEST_SUCCESS
PLUGIN_REMOVE_REQUEST_FAILURE
READER_LIST_UPDATE
READER_LISTS_FOLLOW
READER_LISTS_FOLLOW_SUCCESS
READER_LISTS_FOLLOW_FAILURE
READER_LISTS_UNFOLLOW
READER_LISTS_UNFOLLOW_FAILURE
READER_FOLLOW
READER_TEAMS_RECEIVE
READER_VIEW_STREAM
MAGIC_LOGIN_SHOW_INTERSTITIAL_PAGE
PUBLICIZE_CONNECTIONS_REQUEST_SUCCESS
EXPORT_STATUS_FETCH
POST_COUNTS_RESET_INTERNAL_STATE
SITE_WORDPRESS_UPDATE_REQUEST
READER_STREAMS_SHOW_UPDATES
READER_STREAMS_FILL_GAP
READER_STREAMS_DISMISS_POST
GUIDED_TOUR_UPDATE
POST_TYPE_LIST_LIKES_POPOVER_HIDE
POST_TYPE_LIST_LIKES_POPOVER_TOGGLE
POST_TYPE_LIST_MULTI_SELECTION_MODE_TOGGLE
POST_TYPE_LIST_SELECTION_TOGGLE
POST_TYPE_LIST_SHARE_PANEL_HIDE
POST_TYPE_LIST_SHARE_PANEL_TOGGLE
EDITOR_PASTE_EVENT
LAYOUT_FOCUS_SET
LAYOUT_NEXT_FOCUS_SET
LAYOUT_NEXT_FOCUS_ACTIVATE
SITE_PLANS_FETCH
SITE_PLANS_FETCH_FAILED
SITE_PLANS_REMOVE
SITE_PLANS_TRIAL_CANCEL
SITE_PLANS_TRIAL_CANCEL_COMPLETED
SITE_PLANS_TRIAL_CANCEL_FAILED
THEME_SETUP_TOGGLE_DIALOG
THEME_SETUP_REQUEST
THEME_SETUP_RESULT
FIRST_VIEW_HIDE
COMMENTS_COUNT_INCREMENT
READER_TAGS_REQUEST
READER_TAGS_RECEIVE
READER_UNFOLLOW_TAG_REQUEST
READER_UNFOLLOW_TAG_RECEIVE
READER_FOLLOW_TAG_REQUEST
COMMENTS_WRITE_ERROR
COMMENTS_LIKE
EDITOR_CONTACT_FORM_CLEAR
EDITOR_CONTACT_FORM_LOAD
EDITOR_CONTACT_FORM_FIELD_ADD
EDITOR_CONTACT_FORM_FIELD_REMOVE
EDITOR_CONTACT_FORM_FIELD_UPDATE
EDITOR_CONTACT_FORM_SETTINGS_UPDATE
COMMENTS_UNLIKE
On the other hand, this list look too simple and naïve. I should construct a DAG in which action files points to data-layer files to make it useful for the actual codemod.
Here is a better list, in the form of (data layer handler file): (action dispatchers/creators)
:
{
"client/state/http/index.js": [
"client/state/http/actions.js"
],
"client/state/data-layer/third-party/directly/index.js": [
"client/state/help/directly/actions.js"
],
"client/state/data-layer/third-party/refer/index.js": [
"client/state/refer/actions.js"
],
"client/state/data-layer/wpcom/jetpack-onboarding/index.js": [
"client/state/jetpack-onboarding/actions.js"
],
"client/state/data-layer/wpcom/checklist/index.js": [
"client/state/checklist/actions.js"
],
"client/state/data-layer/wpcom/login-2fa/index.js": [
"client/state/login/actions.js"
],
"client/state/data-layer/wpcom/plans/index.js": [],
"client/state/data-layer/wpcom/theme-filters/index.js": [
"client/state/themes/actions.js"
],
"client/state/data-layer/wpcom/users/index.js": [
"client/state/users/actions.js"
],
"client/state/data-layer/wpcom/timezones/index.js": [
"client/state/timezones/actions.js"
],
"client/state/data-layer/wpcom/privacy-policy/index.js": [
"client/state/privacy-policy/actions.js"
],
"client/state/data-layer/wpcom/read/teams/index.js": [
"client/state/reader/teams/actions.js"
],
"client/state/data-layer/wpcom/posts/revisions/index.js": [
"client/state/posts/revisions/actions.js"
],
"client/state/data-layer/wpcom/users/auth-options/index.js": [
"client/state/login/actions.js"
],
"client/state/data-layer/wpcom/videos/poster/index.js": [
"client/state/ui/editor/video-editor/actions.js"
],
"client/state/data-layer/wpcom/account-recovery/lookup/index.js": [
"client/state/account-recovery/reset/actions.js"
],
"client/state/data-layer/wpcom/account-recovery/request-reset/index.js": [
"client/state/account-recovery/reset/actions.js"
],
"client/state/data-layer/wpcom/read/streams/index.js": [
"client/state/reader/streams/actions.js"
],
"client/state/data-layer/wpcom/account-recovery/reset/index.js": [
"client/state/account-recovery/reset/actions.js"
],
"client/state/data-layer/wpcom/account-recovery/validate/index.js": [
"client/state/account-recovery/reset/actions.js"
],
"client/state/data-layer/wpcom/activity-log/deactivate/index.js": [
"client/state/activity-log/actions.js"
],
"client/state/data-layer/wpcom/activity-log/rewind/index.js": [
"client/state/activity-log/actions.js"
],
"client/state/data-layer/wpcom/sites/activity/index.js": [
"client/state/activity-log/actions.js"
],
"client/state/data-layer/wpcom/sites/blog-stickers/index.js": [
"client/state/sites/blog-stickers/actions.js"
],
"client/state/data-layer/wpcom/domains/countries-list/index.js": [],
"client/state/data-layer/wpcom/me/devices/index.js": [
"client/state/user-devices/actions.js"
],
"client/state/data-layer/wpcom/sites/comment-counts/index.js": [],
"client/state/data-layer/wpcom/sites/media/index.js": [
"client/state/media/actions.js"
],
"client/state/data-layer/wpcom/me/send-verification-email/index.js": [
"client/state/current-user/email-verification/actions.js"
],
"client/state/data-layer/wpcom/me/settings/index.js": [
"client/state/user-settings/actions.js"
],
"client/state/data-layer/wpcom/meta/sms-country-codes/index.js": [],
"client/state/data-layer/wpcom/read/feed/index.js": [
"client/state/reader/feed-searches/actions.js"
],
"client/state/data-layer/wpcom/sites/simple-payments/index.js": [
"client/state/simple-payments/product-list/actions.js"
],
"client/state/data-layer/wpcom/read/recommendations/sites/index.js": [
"client/state/reader/recommended-sites/actions.js"
],
"client/state/data-layer/wpcom/sites/blog-stickers/add/index.js": [
"client/state/sites/blog-stickers/actions.js"
],
"client/state/data-layer/wpcom/concierge/schedules/appointments/index.js": [],
"client/state/data-layer/wpcom/sites/blog-stickers/remove/index.js": [
"client/state/sites/blog-stickers/actions.js"
],
"client/state/data-layer/wpcom/sites/automated-transfer/eligibility/index.js": [
"client/state/automated-transfer/actions.js"
],
"client/state/data-layer/wpcom/sites/automated-transfer/initiate/index.js": [
"client/state/automated-transfer/actions.js"
],
"client/state/data-layer/wpcom/activity-log/rewind/restore-status/index.js": [
"client/state/activity-log/actions.js"
],
"client/state/data-layer/wpcom/me/settings/profile-links/index.js": [
"client/state/profile-links/actions.js"
],
"client/state/data-layer/wpcom/me/transactions/supported-countries/index.js": [],
"client/state/data-layer/wpcom/me/notification/settings/index.js": [
"client/state/notification-settings/actions.js"
],
"client/state/data-layer/wpcom/sites/plugins/new/index.js": [
"client/state/plugins/upload/actions.js"
],
"client/state/data-layer/wpcom/me/block/sites/delete/index.js": [
"client/state/reader/site-blocks/actions.js"
],
"client/state/data-layer/wpcom/me/block/sites/new/index.js": [
"client/state/reader/site-blocks/actions.js"
],
"client/state/data-layer/wpcom/me/settings/profile-links/delete/index.js": [
"client/state/profile-links/actions.js"
],
"client/state/data-layer/wpcom/me/settings/profile-links/new/index.js": [
"client/state/profile-links/actions.js"
],
"client/state/data-layer/wpcom/read/following/mine/delete/index.js": [
"client/state/reader/follows/actions.js"
],
"client/state/data-layer/wpcom/read/sites/posts/follow/index.js": [
"client/state/reader/conversations/actions.js"
],
"client/state/data-layer/wpcom/read/sites/posts/mute/index.js": [
"client/state/reader/conversations/actions.js"
],
"client/state/data-layer/wpcom/read/site/comment-email-subscriptions/delete/index.js": [
"client/state/reader/follows/actions.js"
],
"client/state/data-layer/wpcom/read/site/post-email-subscriptions/new/index.js": [
"client/state/reader/follows/actions.js"
],
"client/state/data-layer/wpcom/read/site/post-email-subscriptions/delete/index.js": [
"client/state/reader/follows/actions.js"
],
"client/state/data-layer/wpcom/read/site/post-email-subscriptions/update/index.js": [
"client/state/reader/follows/actions.js"
],
"client/state/data-layer/wpcom/read/site/comment-email-subscriptions/new/index.js": [
"client/state/reader/follows/actions.js"
],
"client/state/data-layer/wpcom/sites/comments/replies/new/index.js": [],
"client/state/data-layer/wpcom/sites/posts/replies/new/index.js": []
}
Empty array means the action is never dispatched, therefore the handler is never used. There are 58 files here already based on the data gather from my WIP script.
I am now working on a codemod that could automate what 0a9f9e6fdcc787f8a4551e113169b762cf025a7a does on the files above.
While I am working on this I have some second thoughts: the current data-layer middleware design is great in a way that hides itself from components that dispatch the action. The modification here asks developers to actively find out and import
the data-layer handler a said action needs when implementing an action. This is an inferior developer experience.
The other route I have been thinking is to create a babel transform that could inject these imports. Yet I am not sure about how Babel transform can access all the information it needs to do that. Maybe we'll end up having to maintain an action type -> handler file
list statically.
Let me know if I should work on that instead.
@timdream this is great work you are doing. it's also incredibly challenging and intrinsically fragile.
while I like the idea of loading these things asynchronously in theory, I suspect that we risk making the overall experience worse by doing this, and we risk spending a lot of effort in the meantime trying to make it happen.
when I look at the state directory I don't see it maybe as problematic as others do. I think it's quite efficient for what it does. that is, as long as we are hand-coding functions and systems to manage each type of data we have I'm not sure how much better we can get (not saying we can't get better, but the actual size of the state directory is already reasonable for how much it's doing). in other words, how much more overhead and more load-lag are we willing to introduce in order to push this paradigm further?
in #21015 I was able to cut out 7-10 kb just by pruning out the action types file which is totally redundant. I'd like to explore ways we can make these kinds of changes were instead of trying to optimize existing code structures we instead rip them out entirely.
two ways I suspect are viable are by normalizing how we interact with data and by moving data layer code into the server.
by normalizing I mean that there are plenty of cases where all we're doing is "shoving" data from the remote end into the Calypso app. these don't need their own reducers, selectors, and data layer code. it's possible to abstract this away with a kind of constrained remote data system. in #19060 I have explored such an apiData()
system I think could replace many of our directories in the state tree. with that kind of system we could probably eliminate a significant amount of code altogether, which also means our builds are faster in addition to making smaller bundles. the code becomes more standardized and there's less of it to have to understand.
by moving to the server I mean taking the data layer to phase two of its adoption: open up another layer of providers. the wpcom
providers make JSON API calls and handle all the negotiation. we could also make a system whereby we have some kind of /calypso
endpoint on WordPress.com and it takes Redux actions as input and returns a list of Redux actions as a response. for example, maybe we intercept POST_REQUEST
and send it to the /calypso
API endpoint and it sends back { POST_ADD, COMMENT_ADD, COMMENT_ADD, COMMENT_ADD, COMMENT_COUNT_SET }
in response, anticipating related data we want and eliminating the need in Calypso itself to handle all of the logic around translating the POST_REQUEST
into API calls and then transforming the response and making related calls. This would mean that we can entirely eliminate sections of code. Also noteworthy is that adding PHP code on the server doesn't carry the same costs as adding JS to the client does. the server would become coupled to the client and we'd have to maintain the consistency of the data structures, but this is something we already do to some extend and maybe pragmatically is worth it.
lastly, and this would be a really nice boon but it's also a bit more controversial, is that by adding in some kind of sound type system we could eliminate most of our selectors. for all of the code where we are doing nothing but providing a specified interface into the state tree we could replace that by direct calls into state if we had a compiler to provide us the safety which the selectors themselves currently provide. the main difference is that with a type system (like with TypeScript or OCaml) then the constraints are maintained when we write the code and with Babel the constraints are pushed to our customers and maintained at runtime. Also, our selectors don't exactly give us the nice auto-complete that a sound type-system in a well-defined language would.
just some thoughts. this is good work, but I like stepping back and asking if it's the best fit for what we need.
I think we can close this one - @sgomes has been working on state modularization for quite a bit now, and there is an entire project to track this project - https://github.com/Automattic/wp-calypso/projects/95.
I'll move this issue to the project and close it, so we have a reference for this great discussion.
Figure out a way to async-load the pieces of our state engine—reducers/selectors/data-layer—in a way that doesn’t detract from the experience of a global, centralized, and readily available data store. Alternatively, avoid pulling large dependencies on reducers by modularizing further or separating concerns as there is no immediate reason for our state reducers to be a significant drag on code weight.