Skip to content

Conversation

maciekstosio
Copy link
Contributor

@maciekstosio maciekstosio commented Feb 3, 2025

This PR adds first-party support for redux. I resued the enhancer part and WebUI from https://github.com/matt-oakes/redux-devtools-expo-dev-plugin - the code seems to do everything we need with nicely separated communication, except that the communication through the expo/devtools is hardcode. I created a fork of it that's included as a submodule in our repo, but I'm trying to upstream the changes with communication logic abstraction (matt-oakes/redux-devtools-expo-dev-plugin#19) so we can easily keep the fork up to date. The rest (communication through vscode) can be found in software-mansion-labs/redux-devtools-expo-dev-plugin#2).

To archive zero-config support, we utilize window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__, where we pass compose from redux-devtools-expo-dev-plugin with our communication injected. Redux dev tools communicate with the extension through the dev tools, and the extension passes messages through webview events to WebUI.

We need to have redux-devtools-expo-dev-plugin built to import through a wrapper in app runtime. Similarly, the webui needs to be bundled, thus I added packages/vscode-extension/scripts/install-dev-plugins.sh, currently the product of it is committed, and it needs to be run manually, but this is subjected to change.

How Has This Been Tested:

  • Open expo-52-prebuild-with-plugins
  • Comment out configuration related to expo dev plugins
const store = configureStore({
  reducer: rootReducer,
  // devTools: false,
  // enhancers: (getDefaultEnhancers) =>
  //   getDefaultEnhancers().concat(devToolsEnhancer()),
});
  • Reload the app
  • Confirm redux dev tools work

Copy link

vercel bot commented Feb 3, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
radon-ide ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 6, 2025 5:44pm

@maciekstosio maciekstosio changed the title @maciekstosio/first party redux support First-party redux support Feb 3, 2025
Copy link
Collaborator

@filip131311 filip131311 left a comment

Choose a reason for hiding this comment

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

I left some inline comment

this.devtoolsAgent._bridge.addListener(this.scope, this.handleMessages);
};

clearDevToolsAgent = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The pattern we are using in most of the codebase is for setter function to return dispose function that is added to disposables that are called after object destruction , this solution is advantageous, because it does not really on this.scope and this. handleMessages being stable, more over it would be consistant with the rest of the codebase

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tbh, I'm not sure if I see any benefit in it in this. I can't get rid of this.handleMessages as this.clearDevToolsAgent is called also in closeAsync, if it would be just for set/clearDevToolsAgent I totally get it.

this.listeners.set(type, [...currentListeners, listener]);
};

removeMessageListener = (type, listener) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this method seems to never be used

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're implementing methods that could be used by the redux-devtools-expo-dev-plugin - those method match expo interface.


export const isProxyClientReady = () => proxyClient !== null;

export const clearProxyClient = () => proxyClient = null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

shouldn't this method call closeAsync ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Get rid of that in 0ede424


export const clearProxyClient = () => proxyClient = null;

export const createRNIDEProxyClientAsync = async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is it async?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just to follow types from redux-devtools-expo-dev-plugin

const { useEffect } = require("react");
const {createComposeWithDevTools} = require('./external/redux-devtools-expo-dev-plugin');

class RNIDEProxyClient {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit:
This name seems almost too generic i'm not sure what it does from the name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed in: 0ede424


let proxyClient = null;

export const isProxyClientReady = () => proxyClient !== null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there ever a situation in which ProxyClient exist but is not ready? if not i would rename this method to `available if it is possible it should be checked here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, If there isn't redux, or if redux devtools have different configuration. Basically when REDUX_DEVTOOLS_EXTENSION_COMPOSE is not used.

return () => {
clearProxyClient();
Copy link
Collaborator

Choose a reason for hiding this comment

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

is it called when the user opens preview? could you test that and check if it does not cause multiple proxies being created?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed that in 0ede424

@@ -198,6 +198,11 @@
"id": "RadonIDEToolExpoDevPluginReduxDevTools",
Copy link
Collaborator

Choose a reason for hiding this comment

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

why don't we retire the old panel?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We attach to redux like default dev tools on the web, thus if someone have custom configuration, i.e. using dev-plugins, our will stop working. I could try overriding the config, but I'm unsure if I won't break certain setups. So I'd leave it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I was able to reproduce the behavior in which booth panels are open and only one works:

Screenshot 2025-02-07 at 10 15 22

To achieve that just comment and uncomment configuration without restarting the ide:

const store = configureStore({
  reducer: rootReducer,
  // devTools: false,
  // enhancers: (getDefaultEnhancers) =>
  //   getDefaultEnhancers().concat(devToolsEnhancer()),
});

I don't mind leaving the old panel alive if it is the most straight forward solution but we need a way of establishing which one is used and killing the other one.

Copy link
Collaborator

Choose a reason for hiding this comment

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

After additional thought this edge case might be adressed in followup review

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Collaborator

@filip131311 filip131311 Feb 4, 2025

Choose a reason for hiding this comment

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

why this file has suck a strange name is it commit name? wouldn't it be better to use tags as a naming convention?

Copy link
Contributor Author

@maciekstosio maciekstosio Feb 6, 2025

Choose a reason for hiding this comment

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

This is the output from expo export. I don't see a reason to configure a custom build unless the fact that I'm getting those filenames using gs can introduce some problems (?). I assume, the hash at the end is to make sure browser will refreash cache, but that's just my guess.

viewIdPrefix: "RNIDE.Tool.ExpoDevPlugin.ReactQuery",
},
"@dev-plugins/react-native-mmkv": {
label: "MMKV",
label: "MMKV DevPlugin",
viewIdPrefix: "RNIDE.Tool.ExpoDevPlugin.MMKV",
},
"redux-devtools-expo-dev-plugin": {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why don't we retire it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We attach to redux like default dev tools on the web, thus if someone have custom configuration, i.e. using dev-plugins, our will stop working. I could try overriding the config, but I'm unsure if I won't break certain setups. So I'd leave it.

Copy link
Collaborator

@filip131311 filip131311 left a comment

Choose a reason for hiding this comment

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

I left one more important comment

@@ -198,6 +198,11 @@
"id": "RadonIDEToolExpoDevPluginReduxDevTools",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I was able to reproduce the behavior in which booth panels are open and only one works:

Screenshot 2025-02-07 at 10 15 22

To achieve that just comment and uncomment configuration without restarting the ide:

const store = configureStore({
  reducer: rootReducer,
  // devTools: false,
  // enhancers: (getDefaultEnhancers) =>
  //   getDefaultEnhancers().concat(devToolsEnhancer()),
});

I don't mind leaving the old panel alive if it is the most straight forward solution but we need a way of establishing which one is used and killing the other one.

Copy link
Collaborator

@filip131311 filip131311 left a comment

Choose a reason for hiding this comment

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

LGTM

@maciekstosio maciekstosio merged commit 1b071f7 into main Feb 7, 2025
4 checks passed
@maciekstosio maciekstosio deleted the @maciekstosio/first-party-redux-support branch February 7, 2025 10:07
kmagiera added a commit that referenced this pull request Feb 10, 2025
In #931 we introduces a buil-in support for redux plugin that doesn't
rely on expo dev plugins package. This PR however brought a few
potential issues to the codebase, so here we're refactoring it aiming to
fix those problems while also preparing for more steamlined support for
other tools:

1) removing console log statements introduced in #931 that were
polluting the app output
2) clearing up redux registration such that it only relies on the
"compose" callback and doesn't require subsequent calls for setting
devtools agent nor hook-based integration. This is done by accessing the
devtools agent in the same way we do that in the wrapper component.
3) getting rid of direct imports of devtools related code – this for
unknown reason turns out to be causing issues with metro as direct
imports would impact the order in which modules are loaded and hence may
impact the app behavior.
4) we're refactoring module registration such that it is no longer
specific to expo. Before we had registry for expo-related plugins and
now in #931 a separate registration process was introduced for redux
plugin specifically. We're making it more generic and porting both expo
plugins and the redux plugin to use it.

### How Has This Been Tested: 
1. Fire up example app with plugins
2. Test redux plugin shows up on the list when redux is used and doesn't
show up otherwise.
3. Test scenario from #931
maciekstosio added a commit that referenced this pull request Mar 3, 2025
This PR adds first-party react-query dev tools support. It works without
any additional configuration and does not conflict with the Expo Dev
plugin, so the old implementation, which is based on Expo Dev Plugins
(#878), is being removed.

**The solution works as follows:** 
- While transforming the react-query library, we inject a code that
calls `window.__RNIDE_REACT_QUERY_CLIENT_INIT__` with `queryClient` on
mount;
- `__RNIDE_REACT_QUERY_CLIENT_INIT__` broadcasts the apps react-query
state using RN dev tools;
- Radon IDE extension is used as a proxy - receives the events from RN
dev-tools and sends them to React Query DevTools webview;
- Thanks to the above, webview has synced react-query state and displays
it using [original react-query
devtools](https://github.com/TanStack/query/tree/main/packages/query-devtools).

**WebUI**

WebUI needs to be bundled so it can be inserted into webview. For that,
I created a separate repo
https://github.com/software-mansion-labs/radon-ide-react-query-webui -
which consists of
https://github.com/TanStack/query/tree/main/packages/query-devtools with
additional logic required for syncing the state between app and dev
tools. Built artifacts of webui are committed to the webui repo and are
copied while building the Radon IDE extension.

**Communication**

The reason is to sync both states (apps with dev tools one). The sync is
based on
https://github.com/TanStack/query/blob/main/packages/query-broadcast-client-experimental/src/index.ts,
but instead of `BroadcastChannel`, we communicate through RN dev-tools
similarly to redux dev tools #931.

### How Has This Been Tested: 

- run `eas-52-prebuild-with-plugins`
- check if `React Query DevTools`, are available in tools list 
- make sure `React Query DevTools DevPlugin` is not visible
- open `React Query DevTools`
- go to plugins page
- press React Query counter 
- confirm that the events are visible
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants