I have my react app created from vite and there I have my Custom React Error Boundary Component wrap from Components the thing is it cannot catch errors.i debug my error component but it cannot recieve any value in getDerivedStateFromError not componentDidCatch
Here is my Error Boundary Code:
/* eslint-disable #typescript-eslint/no-unused-vars */
import React, { Component } from 'react';
interface IState {
hasError: boolean;
eventId?: string;
}
// eslint-disable-next-line #typescript-eslint/no-empty-interface
interface IProps {
children: any;
}
export default class ErrorBoundary extends Component<IProps, IState> {
constructor(props: Readonly<{}>) {
super(props);
this.state = { eventId: '', hasError: false };
}
static getDerivedStateFromError(error: any) {
console.log('here get Derived');
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error: any, errorInfo: any) {
console.log('My errors', error);
}
render() {
// const { children } = this.props;
console.log('errors');
if (this.state.hasError) {
console.log('errors found', this.state.hasError);
return (
<button
onClick={() =>
console.log("Error Found)
}
>
Report feedback
</button>
);
}
return this.props.children;
}
}
and my app.js code:
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<ErrorBoundary>
<button
onClick={() => {
throw new Error('Im new Error');
}}
>
Click Me
</button>
</ErrorBoundary>
</header>
</div>
);
}
export default App;
does anyone knows what is the issue ?
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)
https://reactjs.org/docs/error-boundaries.html#introducing-error-boundaries
To simulate an error, you need to create a component, make it a child of ErrorBoundary class and click on the button 2 times
function Button() {
const [count, setCount] = useState(0);
const onClick = () => {
setCount(count + 1);
};
useEffect(() => {
if (count === 2) {
throw new Error('I crashed!');
}
});
return (
<button
onClick={onClick}
>
Click Me
</button>
);
}
export default Button;
Related
I'm new to reactjs, and I've just started using state management tool Mobx.
I'd like to show Snackbar using mobx store.
The logic is simple.
When the error was occured, it was added to mobx's observable alert list.
And, the observer component catched that change, and rebuild component.
Here is my code.
// BaseAlert.ts
interface BaseAlert {
message: string;
display: boolean;
}
export interface Success extends BaseAlert {
kind: 'success';
}
export interface Info extends BaseAlert {
kind: 'info';
}
export interface Warn extends BaseAlert {
kind: 'warn';
}
export interface Error extends BaseAlert {
kind: 'error';
}
export type Alert = Success | Info | Warn | Error;
export const alert = (message: string, kind: Alert['kind']): Alert =>
({
message,
kind,
display: true,
} as Alert);
// Alerts.ts
import {just, Maybe, nothing} from 'maybeasy';
import {action, computed, observable} from 'mobx';
import {Alert} from "../base/BaseAlert";
class AlertsStore {
#observable
alerts: Alert[] = [];
#computed
get current(): Maybe<Alert> {
return this.alerts.length === 0 ? nothing() : just(this.alerts[0]);
}
#action
hide = () => {
if (this.alerts.length > 0) {
this.alerts[0].display = false;
}
};
#action
process = () => {
this.alerts = this.alerts.slice(1);
};
#action
push = (alert: Alert) => {
this.hide();
this.alerts.push({...alert, display: true});
};
}
const alertsStore = new AlertsStore();
export default alertsStore;
// ObservableAlert.tsx
import React from 'react';
import alertsStore from "../../hook/AlertsStore";
import {Snackbar} from "#mui/material";
import {observer} from "mobx-react";
#observer
export class Alerts extends React.Component {
render() {
return alertsStore.current.cata({
Just: alert => (
<Snackbar
anchorOrigin={{vertical: 'top', horizontal: 'right'}}
open={alert.display}
onClose={alertsStore.hide}
onDragExit={alertsStore.process}
message={alert.message}
autoHideDuration={1000}
/>
),
Nothing: () => <></>,
});
}
}
And, I added the ObservableAlert component just below the App Component.
// App.tsx
function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/*" element={<HomeView/>}>home</Route>
<Route path="/login" element={<LoginView/>}>login</Route>
</Routes>
</Router>
<Alerts/>
</div>
);
}
When I input username and password in textfield and click the login button, this function is called to push alert into the alertsStore.
async login(username: string, password: string) {
await this.baseEventHandle({
action: async () => {
await this.loginUserUseCase.invoke(username, password);
},
onError: (error) => {
alertsStore.push(alert(error, 'error'));
}
})
}
I checked above function's onError inner function was worked and new Alerts Instance was added to AlertsStore's Alert list.
But, the snackbar did not show at the browser.
Could you tell me what is the problem of my code??
I developed my first React component that was Function based and I'm now trying to refactor it to be Class based. However, I can't seem to get it to work when trying to convert it over. I'm pretty sure that the issue is with the RenderItem method, and when I try to bind it, I get this error: TypeError: Cannot read property 'bind' of undefined. How can I bind a method that's a child of a parent method? Is this possible, and if not what would be a better solution?
Error when compiling:
Line 35:10: 'state' is assigned a value but never used no-unused-vars
Line 51:9: 'renderItem' is assigned a value but never used no-unused-vars
import React, { useEffect, useReducer } from 'react';
import API from '#aws-amplify/api';
import { List } from 'antd';
import { listQuestions as ListQuestions } from '../../graphql/queries';
export default class QuestionLoader extends React.Component {
state = {
questions: [],
loading: true,
error: false,
form: { asked: '', owner: '' },
};
constructor() {
super();
this.GetQuestion = this.GetQuestion.bind(this);
this.reducer = this.reducer.bind(this);
// this.renderItem = this.renderItem.bind(this);
console.log('constructor', this);
}
reducer(state, action) {
switch (action.type) {
case 'SET_QUESTIONS':
return { ...state, questions: action.questions, loading: false };
case 'ERROR':
return { ...state, loading: false, error: true };
default:
return state;
}
}
GetQuestion() {
const [state, dispatch] = useReducer(this.reducer, this.state);
useEffect(() => {
fetchQuestions();
}, []);
async function fetchQuestions() {
try {
const questionData = await API.graphql({ query: ListQuestions });
dispatch({ type: 'SET_QUESTIONS', questions: questionData.data.listQuestions.items });
} catch (err) {
console.log('error: ', err);
dispatch({ type: 'ERROR' });
}
}
const renderItem = (item) => {
console.log(this);
return <List.Item.Meta title={item.asked} description={item.owner} />;
};
}
render() {
return (
<div>
<List loading={this.state.loading} dataSource={this.state.questions} renderItem={this.renderItem} />
</div>
);
}
}
// export default QuestionLoader;
you cannot mix functional component and class component thing.
useEffect, useReducer are wrong one to use with class component.
Don't use bind, use arrow function to create the method. Remove constructor.
import React from 'react';
export default class QuestionLoader extends React.Component {
state = {
data: "name"
};
handleClick = () => {
this.setState({
name:"test"
})
}
render() {
return (<div> {this.state.name}
<button onClick={this.handleClick}></button>
</div>
}
}
I have a component providing a context (=provider) and some children components are consuming the context.
My provider also wraps an ErrorBoundary React component in case of a children crash. From there, if an error happens, the ErrorBoundary React component is updating some values in my context.
But it looks like the value is not update and I don't know why.
Here how my code looks like:
const MyContext = React.createContext<{
state: FsState;
setState: Dispatch<SetStateAction<FsState>> | null;
}>({ state: { ...initState }, setState: null });
const [state, setState] = useState({ ...initState });
const handleError = (error: Error): void => {
setState({
...state,
status: {
...state.status,
hasError: !!error
}
});
};
return (
<MyContext.Provider value={{ state, setState }}>
<ErrorBoundary
onError={handleError}
>
{children}
</ErrorBoundary>
</MyContext.Provider>
);
ErrorBoundary component:
class ErrorBoundary extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { error: null, errorInfo: null };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
this.props.onError(error);
this.setState({
error,
errorInfo
});
}
render(): React.ReactNode {
const { error, errorInfo } = this.state;
const { children } = this.props;
if (errorInfo) {
return (
<>
<div
>
<h1>
Something went wrong.
</h1>
</div>
<div>{children}</div>
</>
);
}
return children;
}
}
And a child component (of my Provider + ErrorBoundary)
export const ChildElement = () => {
const {
state: { status },
} = useContext(MyContext);
if (status.hasError) {
return <div>I crashed but I'm in safe mode</div>;
}
throw new Error("test test"); // trigger an error
return <div>I'll never work</div>;
};
I never enter inside the if loop despite the fact that when debugging, the componentDidCatch is correctly triggered.
Thanks to your playground #Shlang, I found the issue.
This was indeed something deeper. I have another child component having a useEffect and using setState as well to update some other values.
The thing was, it was overriding the hasError:true back to hasError:false because for some asynchronous reason, it was executed after the error is caught by the ErrorBoundary.
I did a workaround by moving the hasError attribute in another useState hook.
Thank you !
I am trying to migrate my previously working local state to redux. Now loading available Players works just fine, but deleting will somehow stop in the playerActions.js file, where I dispatch and then return an API Call. So to further give details here are my code parts in relevance:
PlayerPage.js (Component):
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadPlayers, deletePlayer } from '../../redux/actions/playerActions';
import PlayerForm from './playerform';
import PlayCard from './playercard';
import PropTypes from 'prop-types';
import { toast } from 'react-toastify';
class PlayerPage extends Component {
constructor(props) {
super(props);
this.handleDeletePlayer = this.handleDeletePlayer.bind(this);
state = {};
componentDidMount() {
const players = this.props;
players.loadPlayers().catch(err => {
alert('Loading players failed. ' + err);
});
}
handleDeletePlayer = player => {
toast.success('Player deleted');
try {
deletePlayer(player);
} catch (err) {
toast.error('Delete failed. ' + err.message, { autoClose: false });
}
};
render() {
const styles = {
margin: '20px'
};
return (
<div className="container-fluid">
<div>
<h2 style={styles}>Add Player</h2>
<div className="container-fluid">
<PlayerForm handleAddNewPlayer={this.handleAddPlayer} />
</div>
</div>
<hr></hr>
<div>
<h2 style={styles}>Available Player</h2>
<div className="container-fluid">
{this.props.players.map(player => (
<PlayCard
player={player}
key={player.id}
imageSource={`${process.env.API_URL}/${player.profileImg}`}
onDeletePlayer={this.handleDeletePlayer}
/>
))}
</div>
</div>
</div>
);
}
}
PlayerPage.propTypes = {
players: PropTypes.array.isRequired
};
function mapStateToProps(state) {
return {
players: state.players
};
}
const mapDispatchToProps = {
loadPlayers,
deletePlayer
};
export default connect(mapStateToProps, mapDispatchToProps)(PlayerPage);
And the Action being called is in here:
playerActions.js:
import * as types from './actionTypes';
import * as playerApi from '../../api/playerApi';
export function loadPlayersSuccess(players) {
return { type: types.LOAD_PLAYERS_SUCCESS, players };
}
export function deletePlayerOptimistic(player) {
return { type: types.DELETE_PLAYER_OPTIMISTIC, player };
}
export function loadPlayers() {
return function(dispatch) {
return playerApi
.getAllPlayers()
.then(players => {
dispatch(loadPlayersSuccess(players));
})
.catch(err => {
throw err;
});
};
}
export function deletePlayer(player) {
console.log('Hitting deletePlayer function in playerActions');
return function(dispatch) {
dispatch(deletePlayerOptimistic(player));
return playerApi.deletePlayer(player);
};
}
The console.log is the last thing the app is hitting. But the API Call is never made though.
API Call would be:
playerApi.js:
import { handleResponse, handleError } from './apiUtils';
const axios = require('axios');
export function getAllPlayers() {
return (
axios
.get(`${process.env.API_URL}/player`)
.then(handleResponse)
.catch(handleError)
);
}
export function deletePlayer(id) {
return (
axios
.delete(`${process.env.API_URL}/player/${id}`)
.then(handleResponse)
.catch(handleError)
);
}
I was like spraying out console.log in different places and files and the last one I am hitting is the one in playerActions.js. But after hitting it the part with return function(dispatch) {} will not be executed.
So if someone could point me in a general direction I'd be more than grateful.
It looks like you are calling your action creator deletePlayer but you aren't dispatching it correctly. This is why the console.log is being called but not the method that does the request.
I'd recommend taking a look at the documentation for mapDispatchToProps to fully understand how this works. In your example, you should just need to change the call to deletePlayer in your PlayerPage component to this.props.deletePlayer() to use the action creator after it's been bound to dispatch properly.
this how the mapDispatchToProps should be:
const mapDispatchToProps = dispatch => {
return {
load: () => dispatch(loadPlayers()),
delete: () => dispatch(deletePlayer()),
}
}
then call load players with this.props.load() and delete player with this.props.delete()
I'm making a markdown editor using Marked library like this <div id="preview" dangerouslySetInnerHTML={ {__html: marked('Rendered by **marked**.''></div> but get TypeError: Object(...) is not a function.
Found two relevant posts on SO; first and second I'm using the same syntax as the answers but I get a TypeError. In both posts they used ReactDOM.render() method in the end. Full code:
import React, { Component } from 'react';
import './App.css';
import { Provider, connect } from 'react-redux';
import { createStore } from'redux';
import { marked } from "marked";
// Redux
const ADD = "ADD";
const addText = (text) => {
return {
type: ADD,
text: text
}
};
const textReducer = (state = {text: ''}, action) => {
switch(action.type) {
case ADD:
return Object.assign({},state, { text: action.text })
default:
return state
}
};
const store = createStore(textReducer);
// React
class App extends Component {
constructor(props){
super(props)
/*this.state = {
input : ''
}*/
this.handleChange = this.handleChange.bind(this);
};
handleChange(e){
/*this.setState({
input: e.target.value
})*/
this.props.addText(e.target.value)
};
render(){
return(
<div className="App-header">
<textarea id="editor" value={this.props.text} onChange={this.handleChange}></textarea>
<div id="preview" dangerouslySetInnerHTML={ {__html: marked('Rendered by **marked**.') } }></div>
</div>
)
}
};
// React-Redux
const mapStateToProps = (state) => {
return {
text: state.text
}
};
const mapDispatchToProps = (dispatch) => {
return {
addText: (text) => {
dispatch(addText(text))
}
}
};
const Container = connect(mapStateToProps, mapDispatchToProps)(App);
// eslint-disable-next-line
export default class AppWrapper extends Component {
render() {
return(
<Provider store={store}>
<Container />
</Provider>
);
}
};
The markdown text suppose to be rendered as html in preview element but instead I get TypeError: Object(...) is not a function.
UPDATE: apparently redux was not setup properly and was set to an array instead of object. I fixed that but I still get the same error.
I found the solution, problem was I imported marked as named import {import {marked} from 'marked' instead of import as default like this import marked from 'marked'