Error Handle and Log the errors in React js - reactjs

I'm trying to handle the error and logging the errors in react js
I used errorBoundary method to handle the error but it's only support for the react js version 16
import React, {Component} from "react";
import "./App.css";
import errorimg from './errorimg.svg';
//create a erro boundry
class ErrorLimit extends Component {
constructor(props) {
super(props);
this.state = { error: null, errorInfo: null };
}
//set the error value when invoked
componentDidCatch(error, errorInfo) {
this.setState({
error: error,
errorInfo: errorInfo
})
logErrorToMyService(error, info);
}
render() {
//checked the error
if (!!this.state.errorInfo) {
return (
<div className="snap">
<img src= {errorimg}/>
<div className="snap-message">
{this.state.error && this.state.error.toString()}
{this.state.errorInfo.componentStack}
<p> <b>Error occured - something's gone wrong.</b></p>
<p>Anyway we handled error
</p>
</div>
</div>
);
} else {
return this.props.children;
}
}
}
class Widget extends Component {
constructor(props) {
super(props);
this.state = { loading: true, n: 0 };
this.getCount = this.getCount.bind(this)
}
getCount() {
if (this.state.n > 3) throw new Error('woops..counter betes limit');
return `(${this.state.n})`;
}
handleClick() {
this.setState({ n: this.state.n + 1 });
}
render() {
return (
<div>
<div>Counter widget {this.getCount(this.state.n)}</div>
<button onClick={this.handleClick.bind(this)}>
Click me a few times
</button>
</div>
);
}
}
class SampleApp extends Component {
render() {
return (
<div className="sidebar">
{/* here we used error boundry */}
<ErrorLimit>
<Widget />
</ErrorLimit>
</div>
);
}
}
class App extends Component {
render() {
return (
<div className="app">
<SampleApp />
</div>
);
}
}
export default App;
and I have tried another method for react version 15, It's work fine. Successfully handle the error but can't log the error
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import wrapWithTryCatch from 'react-try-catch-render';
import errorimg from './errorimg.svg';
class MyErrorHandler extends React.Component {
render(){
return (
<div className="App-header">{this.props.error}</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { loading: true, n: 0 };
this.getCount = this.getCount.bind(this)
this.handleClick = this.handleClick.bind(this);
}
getCount() {
if (this.state.n > 3) throw new Error('woops..counter betes limit');
return `(${this.state.n})`;
}
handleClick() {
this.setState({ n: this.state.n + 1 });
}
render(){
return(
<div>
<div>Counter widget {this.getCount(this.state.n)}</div>
<button onClick={this.handleClick.bind(this)}>
Click me a few times
</button>
</div>
);
}
}
export default wrapWithTryCatch(React, MyErrorHandler, {error: "Error catched!"})(App);
Anyone please suggest a method to handle the error and log the error in react version 15

React versions 16.0 and above support error boundaries which can handle the errors.
Below React 16.0, you need to handle errors by state like:
// Create a state variable for error. The initial state is false because there are no errors.
this.state = { hasError: false };
// Set hasError as true when an error is detected.
this.setState({ hasError: true });
if (this.state.hasError){
// Do something, e.g. handle error.
}

Related

Property 'isMounted' does not exist on type 'ErrorBoundary'

I'm practicing typescript and I've run into this bug that won't let me move forward: Property 'isMounted' does not exist on type 'ErrorBoundary'.
I have the following Boundary component
// React
import React from 'react'
// Librarys
import ReportIcon from '#mui/icons-material/Report';
type ChildrenProp = {
children: React.ReactNode,
}
type BoundaryState = {
error: null|string
}
export default class ErrorBoundary extends React.PureComponent<ChildrenProp, BoundaryState> {
constructor(props:ChildrenProp) {
super(props)
this.isMounted = false
this.state = {
error: null,
}
}
componentDidMount() {
this.isMounted = true
}
componentDidCatch(error) {
this.isMounted && this.setState({ error: error.message })
}
componentWillUnmount() {
this.isMounted = false
}
render() {
if (this.state.error) {
return (
<div className="d-flex h-100 align-items-center justify-content-center flex-column text-muted text-center">
<ReportIcon className="mb-1 fs-4" />
<h4 className="text-secondary">Application Error:</h4>
<code className="col-10 mx-auto">{JSON.stringify(this.state.error)}</code>
</div>
)
}
return this.props.children
}
}
I need to understand why Typescript throws me this error, the this.isMounted is the cause of this error, but how can I assign a type?. Thanks a lot
Add public isMounted: boolean; to the component to declare it (the public is optional)
export default class ErrorBoundary extends React.PureComponent<ChildrenProp, BoundaryState> {
public isMounted: boolean;
constructor(props:ChildrenProp) {
...
}

Child component not triggering rendering when parent injects props with differnt values

Here are my components:
App component:
import logo from './logo.svg';
import {Component} from 'react';
import './App.css';
import {MonsterCardList} from './components/monster-list/monster-card-list.component'
import {Search} from './components/search/search.component'
class App extends Component
{
constructor()
{
super();
this.state = {searchText:""}
}
render()
{
console.log("repainting App component");
return (
<div className="App">
<main>
<h1 className="app-title">Monster List</h1>
<Search callback={this._searchChanged}></Search>
<MonsterCardList filter={this.state.searchText}></MonsterCardList>
</main>
</div>
);
}
_searchChanged(newText)
{
console.log("Setting state. new text: "+newText);
this.setState({searchText:newText}, () => console.log(this.state));
}
}
export default App;
Card List component:
export class MonsterCardList extends Component
{
constructor(props)
{
super(props);
this.state = {data:[]};
}
componentDidMount()
{
console.log("Component mounted");
this._loadData();
}
_loadData(monsterCardCount)
{
fetch("https://jsonplaceholder.typicode.com/users", {
method: 'GET',
}).then( response =>{
if(response.ok)
{
console.log(response.status);
response.json().then(data => {
let convertedData = data.map( ( el, index) => {
return {url:`https://robohash.org/${index}.png?size=100x100`, name:el.name, email:el.email}
});
console.log(convertedData);
this.setState({data:convertedData});
});
}
else
console.log("Error: "+response.status+" -> "+response.statusText);
/*let data = response.json().value;
*/
}).catch(e => {
console.log("Error: "+e);
});
}
render()
{
console.log("filter:" + this.props.filter);
return (
<div className="monster-card-list">
{this.state.data.map((element,index) => {
if(!this.props.filter || element.email.includes(this.props.filter))
return <MonsterCard cardData={element} key={index}></MonsterCard>;
})}
</div>
);
}
}
Card component:
import {Component} from "react"
import './monster-card.component.css'
export class MonsterCard extends Component
{
constructor(props)
{
super(props);
}
render()
{
return (
<div className="monster-card">
<img className="monster-card-img" src={this.props.cardData.url}></img>
<h3 className="monster-card-name">{this.props.cardData.name}</h3>
<h3 className="monster-card-email">{this.props.cardData.email}</h3>
</div>
);
}
}
Search component:
import {Component} from "react"
export class Search extends Component
{
_searchChangedCallback = null;
constructor(props)
{
super();
this._searchChangedCallback = props.callback;
}
render()
{
return (
<input type="search" onChange={e=>this._searchChangedCallback(e.target.value)} placeholder="Search monsters"></input>
);
}
}
The problem is that I see how the text typed in the input flows to the App component correctly and the callback is called but, when the state is changed in the _searchChanged, the MonsterCardList seems not to re-render.
I saw you are using state filter in MonsterCardList component: filter:this.props.searchText.But you only pass a prop filter (filter={this.state.searchText}) in this component. So props searchTextis undefined.
I saw you don't need to use state filter. Replace this.state.filter by this.props.filter
_loadData will get called only once when the component is mounted for the first time in below code,
componentDidMount()
{
console.log("Component mounted");
this._loadData();
}
when you set state inside the constructor means it also sets this.state.filter for once. And state does not change when searchText props change and due to that no rerendering.
constructor(props)
{
super(props);
this.state = {data:[], filter:this.props.searchText};
}
If you need to rerender when props changes, use componentDidUpdate lifecycle hook
componentDidUpdate(prevProps)
{
if (this.props.searchText !== prevProps.searchText)
{
this._loadData();
}
}
Well, in the end I found what was happening. It wasn't a react related problem but a javascript one and it was related to this not been bound to App class inside the _searchChanged function.
I we bind it like this in the constructor:
this._searchChanged = this._searchChanged.bind(this);
or we just use and arrow function:
_searchChanged = (newText) =>
{
console.log("Setting state. new text: "+newText);
this.setState({filter:newText}, () => console.log(this.state));
}
Everything works as expected.

Show a react-placeholder skeleton until a image loaded completely in a page

I'm Beginner in react, i want to show a skeleton placeholder until a image loads completely in my page so I used react-placeholder library for skeleton loader and set the loaded state to false so the skeleton loader shows.
I then change the loaded to true by setState it on onLoad function on img tag but my problem is the state is not changing so the skeleton loader shows infinitely thus hiding the image.
how to fix this? what i done wrong here? Someone please help me solve this.
import React from 'react';
import ReactPlaceholder from 'react-placeholder';
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
loaded: false
}
}
handleImageLoaded() {
this.setState({ loaded: true });
}
render() {
return (
<React.Fragment>
<ReactPlaceholder type = 'rect' ready = {this.state.loaded} style = {{height: 160}} >
<div className = "imageHolder" >
<img src ="http://someImage.png" alt = "" onLoad={this.handleImageLoaded.bind(this)} />
</div>
</ReactPlaceholder>
</React.Fragment>
)
}
}
"react-placeholder" is not rendering the image until state.loaded is true, so the onLoad callback will never be executed.
You should take the control of rendering the placeholder and control the image visibility by yourself, and do not use the "react-placeholder" component as it requires a children node, which is not the best election for this case.
import React, { Fragment } from "react";
import ImagePlaceholder from "./ImagePlaceholder";
class Image extends React.Component {
constructor(props) {
super(props);
this.state = {
loaded: false
}
}
handleImageLoaded() {
this.setState({ loaded: true });
}
render() {
const { loaded } = this.state;
const imageStyle = !loaded ? { display: "none" } : {};
return (
<div className = "imageHolder" >
{!loaded && <ImagePlaceholder/> }
<img src ="http://someImage.png" style={imageStyle} onLoad={this.handleImageLoaded.bind(this)} />
</div>
)
}
}
Anyway, if you still want to continue using "react-placeholder", then you could solve it with something like:
import React, { Fragment } from "react";
import ReactPlaceholder from "react-placeholder";
class Image extends React.Component {
constructor(props) {
super(props);
this.state = {
loaded: false
}
}
handleImageLoaded() {
this.setState({ loaded: true });
}
render() {
const { loaded } = this.state;
const imageStyle = !loaded ? { display: "none" } : {};
const image = <img src="http://someImage.png" style={imageStyle} onLoad={this.handleImageLoaded.bind(this)} />;
return (
<Fragment>
<ReactPlaceholder type="rect" ready={this.state.loaded} style={{height: 160}} >
<div className="imageHolder" >
{image}
</div>
</ReactPlaceholder>
{!loaded && image}
</Fragment>
)
}
}

Reactjs this.props.map is not a function

I'm trying to make a simple message app that takes the users name, and a message, then the messages are passed down and displayed in another component. In the component that should display the messages I'm getting an error saying this.props.messages.map is not a function.
Here is my code sandbox:
https://codesandbox.io/s/unruffled-pasteur-nz32o
And here is my actual code:
Parent component:
import React, { Component } from "react";
import Messages from "./Messages";
import Input from "./Input";
export default class Container extends Component {
constructor(props) {
super(props);
this.state = {
messages: {
user: [],
message: []
}
};
}
updateMessage(message, sender) {
this.setState({
messages: [...this.state.messages, { user: sender, message: message }]
});
}
render() {
return (
<div>
<Messages messages={this.state.messages} />
<Input
updateMessage={(message, sender) =>
this.updateMessage(message, sender)
}
/>
</div>
);
}
}
And here is where the messages should be displayed (and also where I am getting the error):
import React, { Component } from "react";
export default class Messages extends Component {
render() {
return this.props.messages.map(message => {
return (
<div>
{message.user}: {message.maessage}
</div>
);
});
}
}
Any ideas what could be causing my error? Thanks!
messages is initialized as an object in state. If you want to map over it, it should be an array. So rather than this:
constructor(props) {
super(props);
this.state = {
messages: {
user: [],
message: []
}
};
}
You'll, want this:
constructor(props) {
super(props);
this.state = {
messages: [
{
user: [],
message: []
}
]
};
}

React: TypeError: this._modal.$el.on is not a function

So I'm new to react and I'm trying to use a boilerplate but I'm getting the following error in the chrome console. Sorry if this is a repeat question but I've tried to google it and found nothing. Thanks for the help in advance.
(Console)
TypeError: this._modal.$el.on is not a function
(index.js)
import React from 'react'
import UIkit from 'uikit'
export default class Modal extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
this._modal = UIkit.modal(this.refs.modal, {
container: false,
center: true
// stack: true
})
console.log(this._modal)
this._modal.$el.on('hide', () => {
this.props.onHide()
})
this._modal._callReady()
if(this.props.visible) {
this._modal.show()
}
}
componentWillReceiveProps(nextProps) {
const { visible } = nextProps
if(this.props.visible !== visible) {
if(visible) {
this._modal.show()
} else {
this._modal.hide()
}
}
}
render() {
return (
<div ref="modal">
{this.props.children}
</div>
)
}
}
It is recommended to handle your modal with your compoenent state and refs are usually used for DOM manipulation.
What you can do is to initialize your state:
constructor(props) {
super(props)
this.state= {
isOpen : false
}
}
in your componentWillReceiveProps:
componentWillReceiveProps(nextProps) {
const { visible } = nextProps
if(this.props.visible !== visible) {
this.setState({
isOpen: visible
})
}
}
and in your render method:
render() {
return (
<div ref="modal">
{this.state.isOpen && this.props.children}
</div>
)
}
Let me know if this issue still persists. Happy to help

Resources