Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/example/src/app/redux/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
});
});
4 changes: 3 additions & 1 deletion apps/example/src/app/redux/store.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { configureStore } from '@reduxjs/toolkit';
import { configureStore, Middleware } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
import { reduxDevToolsMiddleware } from '@dev-plugins/redux/src/useReduxDevTools';

export const store = configureStore({
reducer: {
counter: counterReducer,
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(reduxDevToolsMiddleware),
});

export type RootState = ReturnType<typeof store.getState>;
Expand Down
17 changes: 15 additions & 2 deletions packages/redux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,23 @@ npx expo install @dev-plugins/redux
import { useReduxDevTools } from '@dev-plugins/redux';
import { store } from './store';


export default function App() {
useReduxDevTools(store);

/* ... */
}
```
```

### Add middleware to store

```jsx
import { reduxDevToolsMiddleware } from '@dev-plugins/redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(
rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(reduxDevToolsMiddleware),
);

```
21 changes: 16 additions & 5 deletions packages/redux/src/useReduxDevTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,29 @@ import { Store } from '@reduxjs/toolkit';
*
* @param store - The Redux store.
*/

let lastAction = null;
export function useReduxDevTools(store: Store) {
const client = useDevToolsPluginClient('redux');

useEffect(() => {
client?.sendMessage("storeUpdated", store.getState());
client?.sendMessage('storeUpdated', { store: store.getState(), lastAction });

const unsubscribeFn = store.subscribe(() => {
const state = store.getState();
client?.sendMessage("storeUpdated", state);
client?.sendMessage('storeUpdated', state);
});

return () => {
unsubscribeFn();
}
}, [client])
}
};
}, [client, lastAction]);
}

export const reduxDevToolsMiddleware = (store) => (next) => (action) => {
if (action !== lastAction) {
lastAction = { ...action, time: new Date().toISOString() };
}
const result = next(action);
return result;
};
117 changes: 106 additions & 11 deletions packages/redux/webui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,130 @@
import { App } from 'antd';
import { App, List, Typography, Divider, Tabs, ThemeConfig, ConfigProvider } from 'antd'; // Importing components from Ant Design
import { useDevToolsPluginClient, type EventSubscription } from 'expo/devtools';
import { Fragment, useEffect, useState } from 'react';
import ReactJson from 'react-json-view';

const { Item } = List;
const { Text } = Typography;
const { TabPane } = Tabs;

export default function Main() {
const client = useDevToolsPluginClient('redux');
const [storeHistory, setStoreHistory] = useState<any[]>([])
const [storeHistory, setStoreHistory] = useState<any[]>([]);
const [selectedState, setSelectedState] = useState<any>();

useEffect(() => {
const subscriptions: EventSubscription[] = [];

subscriptions.push(
client?.addMessageListener('storeUpdated', (data) => {
setStoreHistory(prevHistory => [...prevHistory, data]);
if (!data.store) return;
setStoreHistory((prevHistory) => [
...prevHistory,
{ state: data.store, action: data.lastAction },
]);
})
);

return () => {
for (const subscription of subscriptions) {
subscription?.remove();
}
};
}, [client]);


const handleItemClick = (index: number) => {
setSelectedState(storeHistory[index]);
};

const calculateDiff = (stateBefore: typeof selectedState, stateAfter: typeof selectedState) => {
const diff: { [key: string]: any } = {};

if (!stateBefore) return stateAfter;

Object.keys(stateAfter).forEach((key) => {
if (stateBefore[key] !== stateAfter[key]) {
diff[key] = stateAfter[key];
}
});

return diff;
};

return (
<App style={{ width: '100%', height: '100%', padding: '0.75em', overflowY: 'scroll' }}>
{storeHistory.map((history, index) => {
return <Fragment key={index}><ReactJson src={history} /></Fragment>
})}

<App
style={{
display: 'flex',
width: '100%',
height: '100%',
padding: '0.75em',
overflowY: 'scroll',
}}>
<div style={{ flex: 1 }}>
<Divider orientation="left">Action History</Divider>
<List
bordered
dataSource={storeHistory}
renderItem={(item, index) => (
<Item
onClick={() => handleItemClick(index)}
style={{
cursor: 'pointer',
backgroundColor: selectedState === item ? '#f0f0f0' : 'white',
}}>
<Text strong>{index + 1}</Text> {/* Displaying index for clarity */}
&nbsp; &nbsp;
<Text strong>{!item?.action && '@@INIT'}</Text> {/* Displaying initial state */}
<Text strong>{item.action?.type?.toUpperCase()}</Text> {/* Displaying action type */}
<br />
<Text type="secondary">
{item.action?.time && new Date(item.action.time).toLocaleTimeString()}
</Text>
{/* Displaying timestamp */}
</Item>
)}
/>
</div>
<div style={{ flex: 2, marginLeft: '1em' }}>
<Tabs defaultActiveKey="1" tabPosition="left">
<TabPane tab="State" key="1">
<ReactJson src={selectedState?.state} />
</TabPane>
<TabPane tab="Action Details" key="2">
{selectedState?.action ? (
<>
<ReactJson
src={Object.keys(selectedState.state).reduce(
(acc: { [key: string]: any }, key) => {
if (key !== 'time') {
acc[key] = selectedState.state[key];
}
return acc;
},
{}
)}
/>
<Text type="secondary">
Action dispatched at{' '}
{selectedState?.action?.time &&
new Date(selectedState?.action.time).toLocaleTimeString()}
</Text>
</>
) : (
<Text>@@INIT</Text>
)}
</TabPane>
<TabPane tab="Diff" key="3">
{selectedState && (
<ReactJson
src={calculateDiff(
storeHistory[storeHistory.indexOf(selectedState) - 1]?.state,
selectedState.state
)}
/>
)}
</TabPane>
</Tabs>
</div>
</App>
);
}