React ErrorBoundary улавливает ошибку, но ошибка по-прежнему приводит к сбою приложения

#javascript #reactjs #error-handling

#javascript #reactjs #обработка ошибок

Вопрос:

Я пытаюсь создать свою собственную ошибку ErrorBoundary, чтобы перехватывать все приложения и регистрировать их где-нибудь. В настоящее время я использую панель закусок MaterialUI для отображения ошибок. Однако компонент ErrorBoundary регистрирует ошибку, но приложение все равно выходит из строя.

Вот gif-файл того, что я пытаюсь описать. введите описание изображения здесь

Мой код для страницы входа в систему:

 class Login extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            error: { isPresent: false, message: {} },
            password: '',
            email: '',
            validInput: false,
        }
        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleChange = this.handleChange.bind(this);
    }
    static contextType = UserDispatchContext

    componentDidUpdate(prevProp, prevState) {
        this.validateForm();
    }
    validateForm() {
        let valid = this.state.email.length > 0 amp;amp; this.state.password.length > 0;
        if (valid !== this.state.validInput) {
            this.setState({ validInput: valid });
        }
    }
    async handleSubmit(e) {
        //e.preventDefault();
        const { dispatch } = this.context
        const response = await callApi({
            path: "/auth/local", method: "POST", body: {
                identifier: this.state.email,
                password: this.state.password
            }
        })
        if (response.user) {
            dispatch({ type: "LOGIN", user: response.user })
        } else {
            const error = { isPresent: true, message: response }
            this.setState({ error })
        }
    }
    handleChange = (prop) => (event) => {
        this.setState({[prop]: event.target.value });
    };

    render() {
        const { classes } = this.props;
        if (this.state.error.isPresent) {
            throw this.state.error.message
        }

        return (
            <div style={{ width: "100%", height: "100%", display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
                <ReactTitle>Login</ReactTitle>
                <Container component="main" maxWidth="xs">
                    <CssBaseline />
                    <div className={classes.paper}>
                        <Avatar className={classes.avatar}>
                            <SvgIcon>
                                {coffeeBeans}
                            </SvgIcon>
                        </Avatar>
                        <Typography component="h1" variant="h5">
                            Login
                        </Typography>
                        <form className={classes.form}>
                            <Grid container spacing={2}>
                                <Grid item xs={12}>
                                    <TextField
                                        variant="outlined"
                                        required
                                        fullWidth
                                        id="email"
                                        label="Email Address"
                                        name="email"
                                        autoComplete="email"
                                        value={this.state.email}
                                        onChange={this.handleChange('email')}
                                    />
                                </Grid>
                                <Grid item xs={12}>
                                    <TextField
                                        variant="outlined"
                                        required
                                        fullWidth
                                        name="password"
                                        label="Password"
                                        type="password"
                                        id="password"
                                        autoComplete="current-password"
                                        value={this.state.password}
                                        onChange={this.handleChange('password')}
                                    />
                                </Grid>
                                <Grid item xs={12}>
                                    <FormControlLabel
                                        control={<Checkbox value="remember" color="primary" />}
                                        label="Remember me"
                                    />
                                </Grid>
                            </Grid>
                            <Button
                                fullWidth
                                variant="contained"
                                color="primary"
                                className={classes.submit}
                                disabled={!this.state.validInput}
                                onClick={() => this.handleSubmit()}
                            >
                                Login
                            </Button>
                            <Grid container justify="flex-end">
                                <Grid item>
                                    <Link
                                        to="/signup"
                                        variant="body2">
                                        New user? Sign up
                                    </Link>
                                </Grid>
                            </Grid>
                        </form>
                    </div>
                    <Box mt={5}>
                        <Copyright />
                    </Box>
                </Container>

            </div>

        )
    }
}
 

И ошибка:

 class ErrorHandler extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false, open: false, message: {} };
    }
    componentDidCatch(error, info) {
        this.setState({ hasError: true, open: true, message: error });
        console.log("error", error);
    }

    handleClose() {
        this.setState({ hasError: false, open: false })
    }
    parseObjectProperties(obj, parse) {
        for (var k in obj) {
            if (typeof obj[k] === 'object' amp;amp; obj[k] !== null) {
                this.parseObjectProperties(obj[k], parse)
            } else if (obj.hasOwnProperty(k)) {
                parse(k, obj[k])
            }
        }
    }
    getErrorMessage(errorObject) {
        var message = "";
        this.parseObjectProperties(errorObject, function (k, prop) {
            if (k === "message")
                message = prop
        })
        return message;
    }

    render() {
        if (this.state.hasError) {
            return (
                <>
                    {this.props.children}
                    <Snackbar
                        anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
                        open={this.state.open}
                        onClose={this.handleClose}
                        autoHideDuration={4000}
                    >
                        <Alert onClose={this.handleClose} severity="error">
                            {this.getErrorMessage(this.state.message)}
                        </Alert>
                    </Snackbar>
                </>
            )
        }
        return this.props.children;
    }
}
 

Редактировать: я забыл ту часть, где я окружаю свои страницы ошибкой ErrorBoundary.

            <Router>
            <Header />
            <Content >
              <ErrorHandler>
                <Switch>
                  <PublicRoute exact path="/" component={Home} />
                  <PublicRoute restricted path="/login" component={Login} />
                  <PrivateRoute path="/profile" component={Profile} />
                  <PublicRoute restricted path="/signup" component={SignUp} />
                </Switch>
              </ErrorHandler>
            </Content>
          </Router>
 

Я не знаю, что я делаю не так. Кто-нибудь знает, что я делаю не так?

Ответ №1:

Вам не хватает getDerivedStateFromError в вашей границе ошибки, которая обновит ErrorHandler состояние и отобразит Snackbar . Ознакомьтесь с документами react для получения дополнительной информации о границах ошибок.

 class ErrorHandler extends React.Component {
    // ...
    
    static getDerivedStateFromError(error) {
        // Update state so the next render will show the fallback UI.
        return { hasError: true, error }
    }
    
    render() {
        if (this.state.hasError) {
            return (
                <>
                    {this.props.children}
                    <Snackbar
                        anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
                        open={this.state.open}
                        onClose={this.handleClose}
                        autoHideDuration={4000}
                    >
                        <Alert onClose={this.handleClose} severity="error">
                            {this.getErrorMessage(this.state.message)}
                        </Alert>
                    </Snackbar>
                </>
            )
        }
        return this.props.children;
    }
}