How to trigger Sentry.io ErrorBoundary fallback on global errors - reactjs

We are currently adding Sentry.io to our React app and wrapping our root App component with the Sentry.withErrorBoundary() HOC and displaying a fallback ErrorPage. However, I think this only displays for errors that occur within the React Component Tree, and I'm wondering how to display the fallback ErrorPage for Global errors that occur outside the ErrorBoundary. See below for example:
// index.tsx
const AppRoot = withSentryErrorBoundary(App, {
fallback: getErrorPage,
});
ReactDOM.render(
<Provider store={store}>
<IntlProvider locale={locale} messages={messages}>
<AppRoot ... />
</IntlProvider>
</Provider>,
domNode
);
const getErrorPage: FallbackRender = (errorData): React.ReactNode => {
const { foo } = store.getState();
return (<DefaultErrorPage {...foo, ...errorData} />);
};
So I need to figure out 1.) how to hook into the global Sentry error handler so I can 2.) manually trigger the ReactDOM.render() with the ErrorPage component instead of the root App. Can anyone help point me in the right direction? Thanks!

Related

React Component with Scripts That Returns Nothing

I want to have a React component that only has functions in it. These functions should only run once and will run throughout the entirety of the app (these are bluetooth connection functions).
Usually I would create a class for this, but I need to access my Redux store (which can only happen in a component). The component would simply render <></>.
Is there a better way to do this?
You could create a custom hook and use it in your App component. Since you can run other hooks inside, you can access the store with the useStore hook and run code only once with the useEffect hook with an empty array in its dependencies.
function useBluetoothConnections() {
const store = useStore();
useEffect(() => {
// do things once
}, []);
}
function App() {
useBluetoothConnections();
return (<>
Your app here
</>)
}
const rootElement = document.getElementById('root')
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)

Using React-Redux: How to Access Variables in the Store, within Nested Components

I am using React-Redux, but I am not able to figure out how to access a variable in the Redux store inside of my nested components.
How can I share a variable between components, using React-Redux?
For example:
I have an 'index.js' file and 30 nested components. Managing these components becomes difficult after a while.
I have a 'C1.js' component. Let's just say I wrote this code in it.
function Reducer(state = 'example' , action) {
return state;
}
const store = createStore(Reducer)
index.js file:
<Provider store = {store}>
<App/>, document.getElementById('root')
</Provider>
How do I pass the 'store' variable to the 'C1.js' component to the index.js file?
Thanks...
You need to use something called "Connect" to connect your various components to the provider.
In the file that contains your C1.js component:
import {connect} from 'react-redux'
const MyComponent = () => {
let someData = props.someData
return(
//all of your JSX for your component here
)
}
const mapState = state => {
return {
someData: state.someData
}
}
export default connect(mapState)(MyComponent)
In the code above, notice the mapStateFunction. Connect is hooking that up with the Provider, and the state that is on the Provider. So that is where you are able to link whatever properties are on your Provider (React-Redux) state with this particular data.
Now, in your component, you will now have prop.someData
-
In the index file, you have your Provider in the wrong place, you need to change your code to this:
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
)
See, the difference there? The is the React Element (and all of its children that you are asking React to render to the DOM). It is the first parameter of the ReactDOM.render function.
The second parameter to the ReactDom.render function is the element in the DOM where you want it to put all of your React elements.
You did not configure well redux and react. You need to go over the doc of redux to setup correctly. Should get working after that.

ReactJS + Redux subscribe method mess

Using create-react-app
//index.js
...
export const store = createStore(getChange, applyMiddleware(thunk))
//getChange is my reducers name
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
registerServiceWorker();
// Box.js which gets rendered in App.js
import {store} from '../../../index'
...
const renderagain = () => store.getState()
store.subscribe(renderagain)
...
This throws me an error
TypeError: Cannot read property 'subscribe' of undefined
Excuse me? What am I doing wrong?
It's a circular dependency issue.
index.js imports App.js, which eventually imports Box.js. But, Box.js is attempting to import {store} from "../../index.js", which is wrong in several ways.
Your component files should not be attempting to import the store directly, and definitely should not be attempting to import values from index.js. You also shouldn't be trying to subscribe to the store directly in your component files.
Instead, use the React-Redux connect() function in your components to create wrappers that manage the process of subscribing to the store for you. Then, use the connected components, and they'll automatically use the store you passed to <Provider>.
//index.js
...
export const store = createStore(getChange, applyMiddleware(thunk))
//getChange is my reducers name
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
registerServiceWorker();
the code above is good to be used to create a store with some reducers combined. and also providing that tot he whole application by passing it into the Provider Component wrapping the Application. This makes the redux actions and store contents available to access for the component. There you can subscribe the store to et notified on changes in store, or on action dispatches.
You are using the callback to subscribe in a wrong way. You might use it this way:
function select(state) {
return state.some.deep.property
}
​
let currentValue
function handleChange() {
let previousValue = currentValue
currentValue = select(store.getState())
​
if (previousValue !== currentValue) {
console.log(
'Some deep nested property changed from',
previousValue,
'to',
currentValue
)
}
}
​
const unsubscribe = store.subscribe(handleChange)
unsubscribe()
For further documentation and queries you can see this. https://redux.js.org/api/store#subscribe-listener

React router 4 history.listen never fires

Switched to router v4 and history v4.5.1 and now history listener not working
import createBrowserHistory from 'history/createBrowserHistory'
const history = createBrowserHistory()
history.listen((location, action) => {
console.log(action, location.pathname, location.state) // <=== Never happens
})
render(
<Provider store={store}>
<Router history={history}>
...
</Router>
</Provider>,
document.getElementById('root')
)
Any ideas why it is being ignored?
Since you are using BrowserRouter(with import alias Router as mentioned in comments of the question), it doesn't care the history prop you pass in. Instead of that it internally creates and assigns new browser history to the Router. So the history instance that you listen and being used in Router is not the same. That's why your listener doesn't work.
Import the original Router.
import { Router } from 'react-router-dom';
It will work as you expect.
The problem is that you are creating your own history object and passing it into the router. However, React Router v4 already provides this object for you, via this.props. (Importing Router has nothing to do with this)
componentDidMount() {
this.props.history.listen((location, action) => console.log('History changed!', location, action));
}
You may need to layer your app a bit more though, like below, and put this componentDidMount method in your MyApp.jsx and not directly at the very top level.
<Provider store={store}>
<BrowserRouter>
<MyApp/>
</BrowserRouter>
</Provider>
(Or use NativeRouter instead of BrowserRouter if you're doing React Native)
works like a magic: 🧙‍♂️🧙‍♂️🧙‍♂️
const history = useHistory();
useEffect(() => {
if (history) { // for jest sake ))
history.listen(() => {
store.reset();
});
}
}, []);

Basic React Tests Failing after Implementing Redux

I am trying to practice using Redux/testing React, and have a simple app set up with the Facebook Create-React Kit.
I have a basic test that is just supposed to make sure an element renders:
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render( < Companies / > , div);
});
It was passing until I implemented Redux in that element, like so:
function select(state) {
return {companies: state};
}
export default connect(select)(Companies);
Now, despite the element certainly rendering in the browser, and the redux implementation working just fine, I am getting this message in the test results:
Invariant Violation: Could not find "store" in either the context or props of "Connect(Companies)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(Companies)".
I thought I had done this in my routes with this:
let store = createStore(companyApp);
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="/companies" component={Companies}>
<Route path=":id" component={Company}/>
</Route>
< /Route>
</Router>
</Provider>, document.getElementById('root'));
but I must be missing something.
This happens because the Companies component is a connected component.
When you try to render the connected component in the test, it warns you that there is no store present. To solve this you can either wrap the Companies component in a Provider with a store specifically made for the test, or, if you just want to test the rendering of the component without worrying about the redux store you can explicitly export the raw component like so:
export class Companies {
...
}
export default connect(select)(Companies);
and in your test
import { Companies } from ./Companies
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render( < Companies companies={[]} / > , div);
});
Testing connected components is described in more detail here http://redux.js.org/docs/recipes/WritingTests.html#connected-components

Resources