Move pull and push states from SyncSitesProvider to a new Redux slice#2037
Move pull and push states from SyncSitesProvider to a new Redux slice#2037
Conversation
📊 Performance Test ResultsComparing 1cd2659 vs trunk site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
There was a problem hiding this comment.
Push: it seems it stucks at "Creating backup..." step. Tried a few times and also tried in trunk - worked quickly for me.
Pull: not related to the changes, since I reproduce the error in trunk too, but if you are working there - take a look please at this error when pull is finished, maybe it's a quck win:
![]()
…dio-refactor-pull-and-push-states-from-syncsitesprovider
Thanks for reviewing this @nightnei! I took another look at this, and found that earlier we used a ref to keep track of the state, which let us read it back immediately after updating it. For now, I added the ref to the hooks that were previously in the I'm also testing a simpler approach that updated the state objects directly where needed. If it works reliably, I'll update the PR with this approach. |
|
@gcsecsey, I would challenge us to remove With this change, we are preserving functions like |
That's a good point @fredrikekelund, thanks! I'll try to move as much as possible to the Redux slice, and ping for another review. |
Remove the SyncSitesProvider React context layer and migrate all components to use useSyncPull and useSyncPush hooks directly. Changes: - Extract getLastSyncTimeText to useLastSyncTimeText hook - Extract initialization logic to useInitializeSyncStates hook - Move useListenDeepLinkConnection and initialization to App component - Update all components to use hooks directly instead of context - Remove sync-sites-context.tsx
…dio-refactor-pull-and-push-states-from-syncsitesprovider
…dio-refactor-pull-and-push-states-from-syncsitesprovider
|
I carried out the changes to move most of the logic into thunks within @fredrikekelund could you please take another look when you're back? Thanks! |
fredrikekelund
left a comment
There was a problem hiding this comment.
Beautiful work, @gcsecsey! This has come a long way since my last review. It's looking very promising 👍
I got stuck thinking about the cancellation flow for pushSiteThunk and submitted a long suggestion there, which meant I didn't have time to complete the review. I'll pick up on Monday morning where I left off.
Here's my first batch of comments. I've also tested that pulling works as expected. I ran into some trouble with pushing, but didn't have time to debug it yet.
fredrikekelund
left a comment
There was a problem hiding this comment.
I've gone through all of the code now. Again, this is looking very promising 👍
There are a couple of themes that I've looked at, which I think should be cleaned up before landing this:
- Some general cleanup (not exporting functions that are only used locally, removing
asoperators, stuff like that) - Error handling. There are a few opportunities to simplify this. It also ties into my next point…
- Cancellation flows. Dealing with this is pretty gnarly. The cleaner the error handling, the easier this is to address.
- Accepting some more repetition to gain more type safety. Listener middlewares that only run on a single action type offer us guarantees about the action payload, for example, which I think is a good tradeoff.
I've played around a lot with potential suggestion for all of these points. I've left comments and suggestions for most or all of the areas I've identified. Still, this PR will need more testing after applying those comments.
My suggestion is that I wrap up my changes locally tomorrow morning, test it, and then push those. That'll be much more efficient than us going back and forth over Github comments, @gcsecsey. Once I've pushed my changes, we'll both need to test to verify that the app works as expected before landing the PR.
Let me know what you think of that approach, @gcsecsey 🙏
Thanks @fredrikekelund for tirelessly reviewing these changes, and also for the detailed feedback for these! Yeah, that approach would be great, I'd really appreciate if you could push your changes. 🙏 We can also do a huddle to walk over everything and test it together if you want. |
…s-from-syncsitesprovider
|
@gcsecsey, I've pushed a set of changes that revolve around what I mentioned in #2037 (review). I've tested pulling, pushing, and aborting both types of operations at different stages. I suggest we proceed by having you test the sync features as well when you get a chance. Then, we can land the PR once we've determined there are no regressions. |
…s-from-syncsitesprovider
|
I discovered that the ability to manually pause pushes had been lost in the diff, so I restored it. I also went ahead and added zod schemas for the API responses and restored tests that had been botched or skipped. |
There was a problem hiding this comment.
This change (and all the corresponding changes in the other files) was to appease TS. Apparently, TS interfaces can be implicitly merged, which is why the compiler was complaining in apps/studio/src/stores/index.ts when the state interfaces weren't exported. Silly stuff, but this change appeases TS.
…and to cancel other active listeners
…itesprovider' of github.com:Automattic/studio into stu-711-studio-refactor-pull-and-push-states-from-syncsitesprovider
…dio-refactor-pull-and-push-states-from-syncsitesprovider
|
I know it's not easy to see how, but e041e31 breaks sync polling, @gcsecsey. Listeners need to wait for the dispatched actions to finish, because the signal is aborted when the listener completes. Your change might be taken from my previous suggestion. If so, know that I've already implemented everything I suggested. |
Sorry, yes I was trying to implement what you suggested earlier, I didn't realize that you also updated the listeners. I'll revert e041e31. |
… types, and to cancel other active listeners" This reverts commit e041e31.
Related issues
Fixes STU-711
Proposed Changes
Sync operations are currently managed through a React Context (SyncSitesProvider) with two large custom hooks (useSyncPull and useSyncPush). This works, but it has drawbacks: polling is driven by useEffect intervals tied to the component lifecycle, state is only accessible to components wrapped in the provider, and the logic is difficult to test in isolation. The hooks mix business logic (API calls, file operations, error handling) with React state management (useState, useEffect), making them hard to reason about.
This PR replaces the Context + hooks architecture with a single Redux Toolkit slice (sync-operations-slice.ts) and listener middleware:
syncOperationsslice (src/stores/sync/sync-operations-slice.ts): consolidates all pull/push state, async thunks for operations (pushSiteThunk,pullSiteThunk, polling thunks, cancel thunks), 22 selectors, and IPC event listeners into one filesrc/stores/index.ts): replacesuseEffect-based polling intervals. Listeners watch for state changes and automatically dispatch polling thunks after 2s delays, creating self-sustaining loops decoupled from React component lifecycleaddSyncOperation/clearSyncOperation) and error modal display are now handled by listeners reacting to state changes and thunk rejections, rather than being scattered across hooksSyncSitesProviderand hooks (sync-sites-context.tsx,use-sync-pull.ts,use-sync-push.ts): ~1,155 lines removeduseRootSelectorwith selectors anddispatchwith thunks instead ofuseSyncSites()context hookuseLastSyncTimeText(src/hooks/sync-sites/use-last-sync-time-text.ts): small utility previously embedded in the contextconditionfor dedup: polling thunks usecreateAsyncThunk'sconditionoption to skip execution if the operation was cancelled, replacing manual guard checksSyncSitesProviderfromroot.tsx, moved initialization toapp.tsxviainitializeSyncStatesThunkTesting Instructions
This is a pure refactor, no user-facing behavior should change. The goal is to Check that everything still works exactly as before:
Pre-merge Checklist