-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Data module: Add built-in support for persisting stores #8146
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
Changes from all commits
9f78c75
275f723
6b534bb
6adc495
2d87fe0
639d205
436b91e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,22 +47,19 @@ const withHistory = ( options = {} ) => ( reducer ) => { | |
| past: [], | ||
| present: reducer( undefined, {} ), | ||
| future: [], | ||
| lastAction: null, | ||
| shouldCreateUndoLevel: false, | ||
| }; | ||
|
|
||
| let lastAction; | ||
| let shouldCreateUndoLevel = false; | ||
|
|
||
| const { | ||
| resetTypes = [], | ||
| shouldOverwriteState = () => false, | ||
| } = options; | ||
|
|
||
| return ( state = initialState, action ) => { | ||
| const { past, present, future } = state; | ||
| const { past, present, future, lastAction, shouldCreateUndoLevel } = state; | ||
| const previousAction = lastAction; | ||
|
|
||
| lastAction = action; | ||
|
|
||
| switch ( action.type ) { | ||
| case 'UNDO': | ||
| // Can't undo if no past. | ||
|
|
@@ -74,6 +71,8 @@ const withHistory = ( options = {} ) => ( reducer ) => { | |
| past: dropRight( past ), | ||
| present: last( past ), | ||
| future: [ present, ...future ], | ||
| lastAction: null, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just noting, that we are changing the existing logic for all action types. In the past, we would assign I prefer to have them stored as they are in the updated version but I'm trying to understand what was the original issue.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I know but this is a small change that doesn't have any impact but I think it's better this way because when you click the "undo/redo" you don't want to perform a comparison with these actions later, instead, you want to just record a new "past" when you start editing again |
||
| shouldCreateUndoLevel: false, | ||
| }; | ||
| case 'REDO': | ||
| // Can't redo if no future. | ||
|
|
@@ -85,11 +84,16 @@ const withHistory = ( options = {} ) => ( reducer ) => { | |
| past: [ ...past, present ], | ||
| present: first( future ), | ||
| future: drop( future ), | ||
| lastAction: null, | ||
| shouldCreateUndoLevel: false, | ||
| }; | ||
|
|
||
| case 'CREATE_UNDO_LEVEL': | ||
| shouldCreateUndoLevel = true; | ||
| return state; | ||
| return { | ||
| ...state, | ||
| lastAction: null, | ||
| shouldCreateUndoLevel: true, | ||
| }; | ||
| } | ||
|
|
||
| const nextPresent = reducer( present, action ); | ||
|
|
@@ -99,6 +103,8 @@ const withHistory = ( options = {} ) => ( reducer ) => { | |
| past: [], | ||
| present: nextPresent, | ||
| future: [], | ||
| lastAction: null, | ||
| shouldCreateUndoLevel: false, | ||
| }; | ||
| } | ||
|
|
||
|
|
@@ -108,18 +114,20 @@ const withHistory = ( options = {} ) => ( reducer ) => { | |
|
|
||
| let nextPast = past; | ||
|
|
||
| shouldCreateUndoLevel = ! past.length || shouldCreateUndoLevel; | ||
|
|
||
| if ( shouldCreateUndoLevel || ! shouldOverwriteState( action, previousAction ) ) { | ||
| if ( | ||
| shouldCreateUndoLevel || | ||
| ! past.length || | ||
| ! shouldOverwriteState( action, previousAction ) | ||
| ) { | ||
| nextPast = [ ...past, present ]; | ||
| } | ||
|
|
||
| shouldCreateUndoLevel = false; | ||
|
|
||
| return { | ||
| past: nextPast, | ||
| present: nextPresent, | ||
| future: [], | ||
| shouldCreateUndoLevel: false, | ||
| lastAction: action, | ||
| }; | ||
| }; | ||
| }; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| /** | ||
| * External dependencies | ||
| */ | ||
| import { get } from 'lodash'; | ||
|
|
||
| /** | ||
| * WordPress dependencies | ||
| */ | ||
| import deprecated from '@wordpress/deprecated'; | ||
|
|
||
| /** | ||
| * Internal dependencies | ||
| */ | ||
| import { getPersistenceStorage } from './persist'; | ||
|
|
||
| /** | ||
| * Adds the rehydration behavior to redux reducers. | ||
| * | ||
| * @param {Function} reducer The reducer to enhance. | ||
| * @param {string} reducerKey The reducer key to persist. | ||
| * @param {string} storageKey The storage key to use. | ||
| * | ||
| * @return {Function} Enhanced reducer. | ||
| */ | ||
| export function withRehydration( reducer, reducerKey, storageKey ) { | ||
| deprecated( 'wp.data.withRehydration', { | ||
| version: '3.6', | ||
| plugin: 'Gutenberg', | ||
| hint: 'See https://github.com/WordPress/gutenberg/pull/8146 for more details', | ||
| } ); | ||
|
|
||
| // EnhancedReducer with auto-rehydration | ||
| const enhancedReducer = ( state, action ) => { | ||
| const nextState = reducer( state, action ); | ||
|
|
||
| if ( action.type === 'REDUX_REHYDRATE' && action.storageKey === storageKey ) { | ||
| return { | ||
| ...nextState, | ||
| [ reducerKey ]: action.payload, | ||
| }; | ||
| } | ||
|
|
||
| return nextState; | ||
| }; | ||
|
|
||
| return enhancedReducer; | ||
| } | ||
|
|
||
| /** | ||
| * Loads the initial state and persist on changes. | ||
| * | ||
| * This should be executed after the reducer's registration. | ||
| * | ||
| * @param {Object} store Store to enhance. | ||
| * @param {Function} reducer The reducer function. Used to get default values and to allow custom serialization by the reducers. | ||
| * @param {string} reducerKey The reducer key to persist (example: reducerKey.subReducerKey). | ||
| * @param {string} storageKey The storage key to use. | ||
| */ | ||
| export function loadAndPersist( store, reducer, reducerKey, storageKey ) { | ||
| deprecated( 'wp.data.loadAndPersist', { | ||
| version: '3.6', | ||
| plugin: 'Gutenberg', | ||
| hint: 'See https://github.com/WordPress/gutenberg/pull/8146 for more details', | ||
| } ); | ||
|
|
||
| // Load initially persisted value | ||
| const persistedString = getPersistenceStorage().getItem( storageKey ); | ||
| if ( persistedString ) { | ||
| const persistedState = { | ||
| ...get( reducer( undefined, { type: '@@gutenberg/init' } ), reducerKey ), | ||
| ...JSON.parse( persistedString ), | ||
| }; | ||
|
|
||
| store.dispatch( { | ||
| type: 'REDUX_REHYDRATE', | ||
| payload: persistedState, | ||
| storageKey, | ||
| } ); | ||
| } | ||
|
|
||
| // Persist updated preferences | ||
| let currentStateValue = get( store.getState(), reducerKey ); | ||
| store.subscribe( () => { | ||
| const newStateValue = get( store.getState(), reducerKey ); | ||
| if ( newStateValue !== currentStateValue ) { | ||
| currentStateValue = newStateValue; | ||
| const stateToSave = get( reducer( store.getState(), { type: 'SERIALIZE' } ), reducerKey ); | ||
| getPersistenceStorage().setItem( storageKey, JSON.stringify( stateToSave ) ); | ||
| } | ||
| } ); | ||
| } |
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.
Should we put it inside
initializeEditorjust before core blocks get registered?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 don't know, I put it here to keep it close to the previous behavior. The stores are created when the scripts are loaded, so I thought the stores should be initialized (loading the initial value) once the stores are loaded as well.
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.
yeah, it probably doesn't matter that much