vidit-sh / redux-sentry-middleware

Redux middleware for propagating Redux state/actions to use with new @sentry/browser and @sentry/node.
79 stars 16 forks source link

Do I have to re-init Sentry? #15

Closed kristian2x closed 4 years ago

kristian2x commented 4 years ago

I have Sentry.init() in my index.js. Do I have to re-init Sentry in my store.js where I appy the createSentryMiddleware()? or can I just pass the imported sentry (import * as Sentry from "@sentry/browser";)

currently my store.js looks like this (while I do see the fired actions in sentry I get no user context):

import { applyMiddleware, createStore, combineReducers, compose } from "redux";
import * as Sentry from "@sentry/browser";
import reduxPromiseMiddleware from "redux-promise-middleware";
import createSentryMiddleware from "redux-sentry-middleware";

import * as reducers from "./ducks";

// only needed for redux dev tools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const rootReducer = combineReducers(reducers);
const middleware = composeEnhancers(
  applyMiddleware(
    reduxPromiseMiddleware(),
    createSentryMiddleware(Sentry, {
      getUserContext: state => state.user.user
    })
  )
);

export default createStore(rootReducer, middleware);
kristian2x commented 4 years ago

I also tried console.logging state inside getUserContext but it didn't fire, maybe that's a separate problem?

ElyDotDev commented 4 years ago

I faced the same issue. My project is a monorepo, that the redux-store logic is in a separated package (yarn-workspace base packages) than my CRA based app.

The CRA src/index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import * as Sentry from '@sentry/browser';
import App from './app';

Sentry.init({});

ReactDOM.render(
         <App />,
         document.getElementById('root')
);

src/app.js:

import configureStore from '@package/store/configure-store';

const store = configureStore({});

const App = ()=> {
   return <div>The app</div>;
}

@package/store/configure-store.js:

import { applyMiddleware, createStore } from 'redux';
import createSentryMiddleware from 'redux-sentry-middleware';
import * as Sentry from '@sentry/browser';

export default function configureStore(initialState = {}) {
    const sentryMiddleware = createSentryMiddleware(Sentry, {
        getUserContext: (state) => {
            console.log(state);
            return {};
        },
    });
}

But the console.log(state); never called. The issue is because of calling Sentry.init({}); after const store = configureStore({}); which somehow make the below code of this module not effecting the Sentry after calling init. redux-sentry-middleware/blob/master/src/main.js:

Sentry.configureScope(scope => {
      scope.addEventProcessor((event, hint) => {

But the below part will be called because it'll be called after the sentry init as it'll add a breadcrumb for each redux action. redux-sentry-middleware/blob/master/src/main.js:

return next => action => {
      if (filterBreadcrumbActions(action)) {
        Sentry.addBreadcrumb({

This cannot be fixed by putting the Sentry.init({}); call before import App from './app'; because imports will be hoisted You Don't Know JS Yet: ES.Next & Beyond - Chapter 3: Organization. To fix this, you can put the sentry initialize logic into a separate file and import that file before importing App component. Like below: src/sentry.js:

import * as Sentry from '@sentry/browser';
Sentry.init({});

src/index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import  './sentry';
import App from './app';

ReactDOM.render(
         <App />,
         document.getElementById('root')
);
kristian2x commented 4 years ago

Thanks A LOT for the thorough response, I'll try your solution and report back!

Riley-Brown commented 4 years ago

I had my Senty.init in my index as well before I added this middleware, after moving the init into my store before redux middleware, everything was working as expected.

As @alirdn stated, imports are hosisted (which I didn't know so thanks for that ) so the code in your store is running before the init in the index file.

Here's my basic store file

import thunk from 'redux-thunk';
import logger from 'redux-logger';
import reduxPromise from 'redux-promise';
import { createStore, applyMiddleware, compose } from 'redux';

import reducers from './Reducers';

import * as Sentry from '@sentry/browser';
import createSentryMiddleware from 'redux-sentry-middleware';

Sentry.init({
  dsn: YOUR_KEY_HERE,
  beforeSend(event) {
    if (event.exception) {
      Sentry.showReportDialog({ eventId: event.event_id });
    }
    return event;
  }
});

const middleware = [reduxPromise, thunk, createSentryMiddleware(Sentry)];
const composeEnhancers =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(
    reducers,
    composeEnhancers(applyMiddleware(...middleware))
  );

export default store;

And in my index.js

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';

import store from './store';
import App from './App';

render(
  <Provider store={store}>
    <App />
  </Provider>,

  document.getElementById('root')
);
kristian2x commented 4 years ago

Thanks @alirdn your solution worked.