ErrorBoundary not working in functional component with Suspense - reactjs

I am working with a functional component in which I implemented Lazy loading and compoent is lazy loading fine. But in a sanario while there is no component ErrorBoundary is not working and app is breaking with no fallback UI/msg.
My code is
export const SiteContent = () => {
const [selectedOption, setSelectedOption] = useState(null);
let LazyLoadedComponent = null;
if (selectedOption?.component) {
LazyLoadedComponent = React.lazy(
() =>
import(
`./components/${selectedOption.option.value}/${selectedOption.option.value}`
)
);
}
return (
<>
<ErrorBoundary>
<Suspense fallback={<div>Loading....</div>}>
{selectedOption?.component && LazyLoadedComponent && (
<LazyLoadedComponent />
)}
</Suspense>
</ErrorBoundary>
</>
);
};
ErrorBoundary code is
import React from "react";
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;

Try setting your hasError state to true in the componentDidCatch method.
componentDidCatch(error, errorInfo) {
this.setState({ hasError: true});
}

You have to set the hasError state to true for the fallback UI to be shown.
Change your ErrorBoundary's componentDidCatch to :
componentDidCatch(error, errorInfo) {
this.setState({ hasError: true});
}

Related

Value of context is undefiend

I'm a newbie in react, and I'm struggling with something that should be easy.
When a user logs in, I want to save their context and display/hide things on the nav bar.
However, the value of the context is undefined when I pass it to children.
A guide to Context can be found here
Here is my code src/App.js:
export default class App extends Component {
static displayName = App.name;
render() {
return (
<Layout>
<Route exact path='/' component={Login} />
<Route path='/fetch-data' component={FetchData} />
</Layout>
);
}
}
src/userContext:
import React from 'react';
const userContext = React.createContext({ user: {} });
export { userContext };
src/components/Layout.js:
import { userContext } from '../userContext';
export class Layout extends Component {
static displayName = Layout.name;
constructor(props) {
super(props);
this.state = {
user: { 'test123': 'test456' }
};
this.logout = this.logout.bind(this);
}
componentDidMount() {
// get and set currently logged in user to state
}
logout() {
this.setState({ user: {} });
}
render() {
const value = {
user: this.state.user,
logoutUser: this.logout
}
return (
<div>
<userContext.Provider value={value}>
<NavMenu />
<Container>
{this.props.children}
</Container>
</userContext.Provider>
</div>
);
}
}
src/components/NavMenu.js:
import { userContext } from '../userContext';
export class NavMenu extends Component {
static displayName = NavMenu.name;
componentDidMount() {
let value = this.context;
/* perform a side-effect at mount using the value of MyContext */
}
constructor (props) {
super(props);
this.toggleNavbar = this.toggleNavbar.bind(this);
this.state = {
collapsed: true
};
}
toggleNavbar () {
this.setState({
collapsed: !this.state.collapsed
});
}
render() {
if (true) {
return (
<header>
<userContext.Consumer>
{({ value }) => {
console.log('test')
console.log(value);//------------>undefiened
console.log(this.context)
}}
</userContext.Consumer>
...
Is there something I'm missing here
Seems like it can work.
I think the problem is your destructuring value from render props : )
Try:
<userContext.Consumer>
{(value) => {
console.log("test")
console.log(value)
console.log(this.context)
return <b>HELLO</b>
}}
</userContext.Consumer>
Reference for Render props: https://reactjs.org/docs/render-props.html

React converting class component to functional component with static error function

im upgrading an old react pp to use functional components. I am having troubles with the error boundary class component . I simply dont understand how to update the static getDerivedStateFromError what is the correct syntax to update this function?
initial component
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
};
}
static getDerivedStateFromError(_error) {
return { hasError: true };
}
componentDidCatch(error, info) {
sendError("ErrorBoundary", { info }, error);
}
render() {
if (this.state.hasError) {
return <ErrorText />;
} else {
return this.props.children;
}
}
}
New component, which is imssing something for sure as it never sets the error to true, which was done by the static function..
const ErrorBoundary = (props) => {
const [hasError, setHasError] = useState(false)
try {
if (hasError) {
return <ErrorText />;
} else {
return props.children;
}
} catch {
sendError("ErrorBoundary", { info }, error);
}
}
There's no way to do the componentDidCatch nor the getDerivedStateFromError on hooks right now. Here's the documentation:
https://reactjs.org/docs/hooks-faq.html#from-classes-to-hooks
getSnapshotBeforeUpdate, componentDidCatch and getDerivedStateFromError: There are no Hook equivalents for these methods yet, but they will be added soon.

LoginPage in React with Redirecting

I have response from server about auth status current user. Based on this information I'm rendering HomePage or LoginPage. How do I can redirect all unauthorized users to '/login' url and others to homepage. When I`m using history.push('/login') it saves permament in url, because in first rendering auth status is always false
import React from 'react';
import './App.scss';
import MainPage from './components/MainPage/MainPage';
import { withRouter, Route } from 'react-router-dom';
import { getAuthStatusThunk } from './redux/authReducer';
import { compose } from 'redux';
import { connect } from 'react-redux';
import Preloader from './components/commons/Preloader/Preloader';
class App extends React.Component {
constructor() {
super()
this.state = { isCheckingLogin: false }
this.getAuthStatusThunk = this.props.getAuthStatusThunk.bind(this)
}
componentDidMount() {
getAuthStatusThunk()
}
async getAuthStatusThunk() {
this.setState({ isCheckingLogin: true })
let res = await (checkIsLoggedIn())
if (res.isLoggedIn) {
this.setState({ isCheckingLogin: false })
}
else {
this.props.history.push('/login')
this.setState({ isCheckingLogin: false })
}
}
render() {
return (
<React.Fragment>
{this.isCheckingLogin ? <Preloader isLoading={true} /> : <MainPage />}
</React.Fragment>
)
}
}
let mapStateToProps = (state) => ({
isAuth: state.authPage.isAuth,
successStatus: state.authPage.successStatus
})
let FunctionRender = compose(
withRouter,
connect(mapStateToProps, { getAuthStatusThunk }))(App)
export default FunctionRender;
You can keep loading and error value also in the state.
Then check if any one of the state value is set or not.
Based on the state value navigate the user accordingly.
You can set a state isCheckingLogin to true while you call the getAuthStatusThunk. Here I include a sample snippet for implementing it.
class App extends React.Component {
constructor() {
super()
this.state = { isCheckingLogin: false }
this.getAuthStatusThunk = this.getAuthStatusThunk.bind(this)
}
componentDidMount() {
getAuthStatusThunk()
}
async getAuthStatusThunk() {
this.setState({ isCheckingLogin: true })
let res = await (checkIsLoggedIn())
if (res.isLoggedIn) {
this.setState({ isCheckingLogin: false })
}
else {
this.props.history.push('/login')
this.setState({ isCheckingLogin: false })
}
}
render() {
return (
<React.Fragment>
{isCheckingLogin ? <Loader /> : <MainPage />}
</React.Fragment>
)
}
}
The Loader component can be used for showing a spinner or some sort of animation while login status is checked if you want.

ErrorBoundary not catching error thrown by imported function

I have a component wrapped in an Error Boundary, when a button is clicked a validate() function is called, this function throws an error if there is not information provided however the ErrorBoundary is not catching this error.
Render function on Component
return (
<ErrorBoundary>
...
<Playlist
...
onClick={this.addPlaylistToSpotify} // this function can throw an error
...
/>
</ErrorBoundary>
);
Function with error
addPlaylistToSpotify = () => {
try {
addPlaylist(this.state.newPlaylist); // this function throws an error on validate()
} catch (error) {
throw new Error(error);
}
...
};
ErrorBoundary Component
import React, { Component } from "react";
import { ErrorOverlay } from "../../components/index";
import styles from "./ErrorBoundary.css";
export class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: ""
};
}
componentDidCatch(error, errorInfo) {
this.setState({
hasError: true,
error: error,
errorInfo: errorInfo
});
// TODO: log the error somewhere in the db
}
dismiss() {
this.setState({
hasError: false,
error: null,
errorInfo: ""
});
}
render() {
if (this.state.hasError) {
return (
<ErrorOverlay message={this.state.errorInfo} dismiss={this.dismiss} />
);
} else {
return this.props.children;
}
}
}
Any help would be hugely appreciated, thanks!
Any help would be hugely appreciated. Thanks!
From React docs
https://reactjs.org/docs/error-boundaries.html#how-about-event-handlers
Note
Error boundaries do not catch errors for:
Event handlers
Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
Server-side rendering
Errors thrown in the error boundary itself (rather than its children)
In your code, the error is thrown from an event handler (addPlaylistToSpotify) so componentDidCatch can't catch the error. Therefore you need to do something like this:
import React from 'react';
export class Playlist extends React.Component {
constructor (props) {
super(props);
this.state = {
error: false
// ...
}
}
addPlaylistToSpotify = () => {
try {
// Something throws an error here.
} catch (error) {
this.setState({ error: true });
}
}
render() {
if (this.state.error) {
throw new Error('I crashed!');
}
return (
<div>
...
<button onClick={this.addPlaylistToSpotify}>Add song</button>
...
</div>
)
}
}
I hope this helps.

How can i error handling in react?

I searched for error handling in react.
and i found componentDidCatch.
but componentDidCatch only catch in render.
I want to catch a error in method.
My source
import React from 'react';
class Child extends React.Component{
handleError = () => {
throw new Error('Error');
}
render() {
return (
<button onClick={this.handleError}>Error</button>
);
}
}
export default Main extends React.Component {
componentDidCatch(error,info) {
console.log(error, info); // NOT WORK
}
render() {
return (<Child/>);
}
}
You can check official React's Error handeling page where they use this code:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
this codebox that is official Dan Abramov's (creator of React) CodeBox: https://codepen.io/gaearon/pen/wqvxGa?editors=0010

Resources