首页 > 解决方案 > 在 react/redux 应用程序中显示成功和错误消息

问题描述

我正在尝试向我的应用程序添加 toast 通知,我一直在尝试使用的一个插件是 react-toastify。

我遇到的问题可能更多的是一般的 react/redux 问题,而不是 react-toastify 之类的插件。

我正在使用 reducer 为错误和成功消息设置 redux 状态,根据我对当前代码的理解,每个错误或成功消息都在存储中持久存在,直到调用另一个操作来清除它们。

我无法弄清楚的问题是我如何只触发一次敬酒。例如。我输入了错误的凭据,它会创建一个错误的 toast,但是每当状态更改并重新加载(在电子邮件或密码字段中输入任何内容)时,它都会创建另一个 toast。

如何让它只显示一次?

userActions.js

function handleErrors(res) {
    if (res.ok) {
        return res.json();
    } else {
       return res.json().then(err => {throw err;});
    }
}

export const login = (user) => dispatch => {
    fetch(`${url}/login`, 
    {
        credentials: 'include', 
        method: 'post', 
        body: user, 
        headers: new Headers({
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        })
     })
     .then(handleErrors)
     .then(res =>
         dispatch({
             type: LOGIN,
             payload: res
         })
     )
     .catch(error => 
         dispatch({
             type: ERROR,
             payload: error
         })
     )
}

userReducer.js

const initialState = {
    errors: '',
    success: ''
};

export default function(state = initialState, action) {
    switch (action.type) {

        case LOGIN:
            return {
                ...state,
                errors: '',
                success: action.payload.message
            };

        case ERROR:
            return {
                ...state,
                success: '',
                errors: action.payload.message
            }

        default:
            return state;
        }
}

应用程序.js

app.post('/login', function(req, res) {
    ... return res.status(500).send({ message: 'Wrong credentials' });
    ... return res.status(200).send({ message: 'good!' });
});

登录.js

class Login extends React.Component {
    constructor() {
        super();    
        this.state = {
            email: "",
            password: ""
        }
    }

    handleChange = event => {
        this.setState({
            [event.target.id]: event.target.value
        });
    }

    render() {

        const { errors, login, success } = this.props;

        if (success !== '') toast.success(success, {
            position: toast.POSITION.TOP_CENTER
        });

        if (errors !== '') toast.error(errors, {
            position: toast.POSITION.TOP_CENTER
        });

        return (
            <div>
                <input type="text" id="email" placeholder="Email Address" onChange={this.handleChange} />
                <input type="password" id="password" placeholder="Password" onChange={this.handleChange} />
                <button onClick={() => login(JSON.stringify({email: this.state.email, password: this.state.password}))}>Log In</button>
                <ToastContainer />  
            </div>  
        )
    }
}

const mapStateToProps = state => ({
    errors: state.store.errors,
    success: state.store.success
});

export default connect(mapStateToProps, {login})(Login);

标签: reactjsreduxreact-redux

解决方案


您在 render 中调用 toast.success 或 toast.error ,每次重新渲染组件时都会弹出一个新的 toast。

解决方案很简单。将你的 toast 调用移到渲染之外,它们只会被调用一次。

实现此目的的一种方法是从您的 userAction 返回一个值。

export const login = (user) => dispatch => {
    return new Promise((resolve, reject) => {
        fetch(`${url}/login`, 
        {
            credentials: 'include', 
            method: 'post', 
            body: user, 
            headers: new Headers({
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            })
         })
         .then(handleErrors)
         .then(res => {
                 dispatch({
                     type: LOGIN,
                     payload: res
                 })
                 resolve(res)
             }
         )
         .catch(error => {
                 dispatch({
                     type: ERROR,
                     payload: error
                 })
                 reject(error)
             }
         )
    }
}

然后使用该值在 login.js 中烤面包。

class Login ... {
    ...
    loginUser = () => {
        this.props.login(JSON.stringify({email: this.state.email, password: this.state.password}))
        .then(res => {
                toast.success(res.message, { position: toast.POSITION.TOP_CENTER })
            }
        ).catch(error => {
                toast.error(error.message, { position: toast.POSITION.TOP_CENTER })
            }
        )
    }
    ...
    render() {
        return (
            ...
            <button onClick={this.loginUser}>Log In</button>
            ...
        )
    }
}

还有其他方法可以实现相同的功能,并且根据项目的结构,您可能希望以更通用的方式敬酒。


推荐阅读