【问题标题】:How should I use React to display global error messages?我应该如何使用 React 来显示全局错误消息?
【发布时间】:2020-06-16 05:33:59
【问题描述】:

我在顶层有一个 App 组件,然后在该组件中,我有一个名为 ErrorMessages 的组件。

我将 App 组件的状态放入上下文中,然后在我的应用程序的其他位置,如果发生错误,我会在 App 组件的上下文中设置错误。

App 组件随后会通过 prop 将错误传递给ErrorMessages,然后会显示错误消息。

问题是,我似乎无法清除错误消息。

目前我尝试的是,使用useEffect的清理机制来清除errorMessages。

假设我有一个名为UserRegistration 的组件,用户没有输入电子邮件地址,我设置了一个错误“需要电子邮件地址”,这样显示就好了。

现在,当UserRegistration 被卸载时,我想从App 上下文中清除错误消息。

我怎样才能做到这一点?

这是我的应用组件:

class App extends React.Component {
    // For errors in the format of:
    // [{message: "first_name missing..."}, {message: "Email address in use..."}]
    setErrorMessages = (errors) => { 
        this.setState({errors}) }
    // Other messages not in the format above. Can set "alert-warning" or
    // "alert-success" type messages.
    setStatusMessage = (statusType, statusMessage) => {
        this.setState({statusType, statusMessage}) }

    clearAllMessages = () => {
        this.setState({errors: [], statusType: null, statusMessage: null}) }


    setAuthToken = () => {}
    setLoaded = (val) => { this.setState({loaded: val}) }


    state = {
        user: {},

        loaded: false,
        errors: [],
        statusType: null,
        statusMessage: null,

        setErrorMessages: this.setErrorMessages,
        clearAllMessages: this.clearAllMessages,
        setStatusMessage: this.setStatusMessage,

        setLoading: this.setLoading,
        setLoaded: this.setLoaded,
        setAuthToken: this.setAuthToken,
    }

    render() {
        return (
            <div className="App container">
                <React.StrictMode>
                    <AppContext.Provider value={this.state}>
                        <BrowserRouter>
                            {this.state.statusMessage ? 
                                <div className={"alert alert-" + this.state.statusType}>{this.state.statusMessage}</div> : null}
                            <ErrorMessages list={this.state.errors} />
                            {!this.state.loaded ? <LoaderWidget /> : null }
                            <IssueBrowser />
                        </BrowserRouter>
                    </AppContext.Provider>
                </React.StrictMode>
            </div>
        );
    }
}

如果我在较低级别的组件中使用useEffect 的清理机制,则会出现“超出最大深度”错误,并且页面会挂起。

这是我调用 useEffect 的组件:

const UserRegistration = (props) => {
    const appContext = useContext(AppContext)
    const history = useHistory();

    const registerUser = (event) => {
        event.preventDefault()
        appContext.setLoaded(false)

        let payload = {
            first_name: event.target.first_name.value,
            last_name: event.target.last_name.value,
            email_address: event.target.email_address.value,
            password: event.target.password.value }

        Users.create(payload)
            .then(response => { 
                appContext.setStatusMessage("success", "User successfully registered.")
                appContext.setLoaded(true)
                history.push('/')
            })
            .catch(error => { 
                if (error.response.data.messages) { appContext.setErrorMessages(error.response.data.messages) }
                appContext.setLoaded(true)
            })
    }

    useEffect(() => {
        return function cleanup() {
            appContext.clearAllMessages()
            // this just seems to hang, the app goes into an infinite loop at this point and freezes.
        }
    })

    return (
        <form onSubmit={registerUser}>
            <div className="form-group">
                <label htmlFor="first_name">First Name:</label>
                <input name="first_name" type="text" className="form-control" />
            </div>
            <div className="form-group">
                <label htmlFor="last_name">Last Name:</label>
                <input name="last_name" type="text" className="form-control" />
            </div>
            <div className="form-group">
                <label htmlFor="email_address">Email Address:</label>
                <input name="email_address" type="text" className="form-control" />
            </div>
            <div className="form-group">
                <label htmlFor="password">Password:</label>
                <input name="password" type="password" className="form-control" />
            </div>
            <div className="form-group">
                <label htmlFor="password">Confirm Password:</label>
                <input name="password" type="password" className="form-control" />
            </div>
            <div className="form-group">
                <input className="btn btn-primary form-control" type="submit" value="Register" />
            </div>
        </form>
    )
}

【问题讨论】:

  • 你提到了一些关于 useEffect 的事情,但你没有展示它。对了,你看error boundaries了吗? reactjs.org/blog/2017/07/26/…
  • 那是因为我知道我的方法行不通,而且我不想费心去尝试让它发挥作用,因为它可能构思不周。从概念上讲,我想知道其他人如何实现全局错误消息组件之类的东西。从高层次的角度来看,经验丰富的 React 开发人员如何实现这样的目标?可能不是我这样做的方式,所以我们不要为此烦恼。我发布了更多作为我想要实现的目标的示例。
  • 对我来说这看起来不错。听起来您需要一个地方来存储所有(?)您的错误消息,以便您可以在根级别有条件地渲染内容,并且使用context 可以正常工作。听起来您在重复更新子组件的 useEffect 挂钩内的全局状态时遇到了问题,所以这就是您应该关注的问题。
  • 我添加了更多代码,特别是我正在使用的组件useEffect,据我所知,这应该可以工作,但似乎一切都停止了,它挂起,并创建一个无限循环。
  • 你有无限循环,因为 useEffect 自己造成了一个效果,它自己触发了。

标签: reactjs


【解决方案1】:

使用事件!当用户关闭事件时发送 cleanAllErrors。 以同样的方式监听 ErrorHandlingComponent 中的错误并显示错误。

如果您想让错误在超时后消失,那很好。对于某些错误是可以的。同样在这里发送消息。如果您希望用户对某些重要错误进行交互,请在错误标签的 onClose 中发送消息!

干杯

【讨论】:

    【解决方案2】:

    您获得maximum depth exceeded 的原因是因为每次收到新的props 或通过hooks 获得新数据时,UserRegistration 组件内的useEffect 挂钩都会运行。

    当您的 cleanup 函数被触发时会发生这种情况,因为它会更新您的 context 并因此导致无限重新渲染。

    您应该传递一个空的依赖数组,以便useEffect 挂钩的行为类似于class component 中的componentWillUnmount 生命周期方法:

    const UserRegistration = (props) => {
      // ...
    
      useEffect(() => {
        return function cleanup() {
          appContext.clearAllMessages();
        }
    
        // b/c of the empty dependency array
        // this hook will only run once when
        // the component renders for the first time, and 
        // it will run the cleanup function once, 
        // when it unmounts
      }, []);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-07
      • 1970-01-01
      • 2019-12-29
      • 1970-01-01
      • 2018-06-14
      • 2019-02-20
      相关资源
      最近更新 更多