Skip to content

Data Module: Restrict the state access to the module registering the reducer only#4058

Merged
youknowriad merged 1 commit intomasterfrom
update/restrict-global-state-access
Dec 20, 2017
Merged

Data Module: Restrict the state access to the module registering the reducer only#4058
youknowriad merged 1 commit intomasterfrom
update/restrict-global-state-access

Conversation

@youknowriad
Copy link
Copy Markdown
Contributor

This PR updates the API of the "data" module to restrict the access to the global state.
The module registering a reducer receives a "store" object as a return value to the registerReducer call.

Coming next

  • An official way to expose a module's data to other modules.

Testing instructions

  • Test that the editor loads and saves posts properly.

@youknowriad youknowriad added the Framework Issues related to broader framework topics, especially as it relates to javascript label Dec 18, 2017
@youknowriad youknowriad self-assigned this Dec 18, 2017
@youknowriad youknowriad requested review from aduth, gziolo and mcsf December 18, 2017 09:07
@youknowriad
Copy link
Copy Markdown
Contributor Author

cc @atimmer

Copy link
Copy Markdown
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

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

An official way to expose a module's data to other modules.

Do you have any ideas to share about how this looks? I would have thought one of the ideas with a global store is that the state is easily accessible from anywhere in the system.

data/index.js Outdated
export const subscribe = store.subscribe;
return {
dispatch: store.dispatch,
getState: () => get( store.getState(), key ),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is get necessary here (or anywhere we're calling getState())?

Copy link
Copy Markdown
Member

@gziolo gziolo Dec 20, 2017

Choose a reason for hiding this comment

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

You can introduce the following helper method:

const getState = () => get( store.getState(), key );

and use in all places inside registerReducer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I guess it's not necessary after all

@gziolo
Copy link
Copy Markdown
Member

gziolo commented Dec 18, 2017

Do you have any ideas to share about how this looks? I would have thought one of the ideas with a global store is that the state is easily accessible from anywhere in the system.

Yes, I would love to see that explained. I wanted to integrate it for coediting branch today, but I had no clue how to register local state and access it from Redux afterward. I would love to see some example with the code to better understand the idea.

data/README.md Outdated
If your module or plugin needs to store and manipulate client-side data, you'll have to register a "reducer" to do so. A reducer is a function taking the previous `state` and `action` and returns an update `state`. You can learn more about reducers on the [Redux Documentation](https://redux.js.org/docs/basics/Reducers.html)

### `wp.data.getState()`
This function returns a Redux-like store object with the following methods:
Copy link
Copy Markdown
Member

@gziolo gziolo Dec 18, 2017

Choose a reason for hiding this comment

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

wp.data.registerReducer - should it have a name passed as a first param? I guess it should. I noticed it today, when reading docs.

@atimmer
Copy link
Copy Markdown
Member

atimmer commented Dec 18, 2017

I think this PR can go in. If we desire to have the store globally available later, we can always revert this. Adding more APIs is easier than restricting. I'm assuming

An official way to expose a module's data to other modules.

will come very soon after this PR has been merged.

@youknowriad
Copy link
Copy Markdown
Contributor Author

Do you have any ideas to share about how this looks?

I have rough ideas and some POCs. And it's two separate ideas:

  • register a subtree of resolvers and provide a GraphQL API (Provider and query HoC)
  • register selectors and provide a (Provider and withSelectors HoC)

I may submit a POC tomorrow.


registerReducer( 'core/editor', withRehydratation( reducer, 'preferences' ) );
let store = registerReducer( 'core/editor', withRehydratation( reducer, 'preferences' ) );
store = applyMiddlewares( store );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we have 3 enhancers in here?:

  • applyMiddlewares
  • loadAndPersist
  • enhanceWithBrowserSize

Can we further refactor this code to make it more obvious that we modify store instance?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The store instance is only modified in applyMiddlewares

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right, what about having:

const store = applyMiddlewares(
    registerReducer( 'core/editor', withRehydratation( reducer, 'preferences' ) )
);
enhanceWithLoadAndPersist( store, 'preferences', STORAGE_KEY, PREFERENCES_DEFAULTS );
enhanceWithBrowserSize( store );

return {
dispatch: store.dispatch,
getState: () => get( store.getState(), key ),
subscribe( listener ) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm trying to understand why using store.subscribe is not enough here. When I look at Redux source code, I see that listener is executed only at the end of the dispatch call: https://github.com/reactjs/redux/blob/master/src/createStore.js#L194. This code tries to filter out all listener calls when the local state hasn't changed. It seems like a good idea, but the question is how it works in practice? Is Redux really immutable here? It might be not architected this way to speed up building new state.

Copy link
Copy Markdown
Contributor Author

@youknowriad youknowriad Dec 20, 2017

Choose a reason for hiding this comment

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

The more we'd have module and plugins using the "data" module, the more we'd have actions handled only by a separate reducer which would create unnecessary listener calls for most modules/plugins. I think this is a necessary performance improvement.

Not noticeable right now because we only have one registered reducer.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Okey, I looked into combineReducers code. It seems to be smart enough to return old state when nothing has changed. See: https://github.com/reactjs/redux/blob/master/src/combineReducers.js#L118.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, but in this case the state did change but only for one module.

Copy link
Copy Markdown
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

This change makes a lot of sense. I left some comments when trying to understand how it is supposed to work. Feel free to ignore my code refactorings proposed, if you don't like them. They helped me to understand better what is happening, but it might be an individual thing.

The only thing that needs to be updated before merging is this issues I raised in the comment for docs: https://github.com/WordPress/gutenberg/pull/4058/files#r157513975.

@youknowriad youknowriad force-pushed the update/restrict-global-state-access branch from 7b6964a to ee520c3 Compare December 20, 2017 10:32
@youknowriad youknowriad merged commit 34a13d3 into master Dec 20, 2017
@youknowriad youknowriad deleted the update/restrict-global-state-access branch December 20, 2017 10:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Framework Issues related to broader framework topics, especially as it relates to javascript

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants