-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Data Module: Restrict the state access to the module registering the reducer only #4058
Conversation
cc @atimmer |
There was a problem hiding this 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 ), |
There was a problem hiding this comment.
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()
)?
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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
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
@@ -10,17 +10,16 @@ This module holds a global state variable and exposes a "Redux-like" API contain | |||
|
|||
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: |
There was a problem hiding this comment.
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.
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
will come very soon after this PR has been merged. |
I have rough ideas and some POCs. And it's two separate ideas:
I may submit a POC tomorrow. |
editor/store/index.js
Outdated
|
||
/** | ||
* Module Constants | ||
*/ | ||
const STORAGE_KEY = `GUTENBERG_PREFERENCES_${ window.userSettings.uid }`; | ||
|
||
registerReducer( 'core/editor', withRehydratation( reducer, 'preferences' ) ); | ||
let store = registerReducer( 'core/editor', withRehydratation( reducer, 'preferences' ) ); | ||
store = applyMiddlewares( store ); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 );
data/index.js
Outdated
return { | ||
dispatch: store.dispatch, | ||
getState: () => get( store.getState(), key ), | ||
subscribe( listener ) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
7b6964a
to
ee520c3
Compare
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
Testing instructions