本文介绍了如何在React/Redux/Typescript通知消息中从自身上卸下,取消渲染或删除组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道这个问题已经被问过几次了,但是在大多数情况下,解决方法是在父级中解决这个问题,因为责任流只是在下降.但是,有时您需要通过一种方法杀死组件.我知道我无法修改其道具,并且如果我开始添加布尔值作为状态,那么对于一个简单的组件而言,它将会变得非常混乱.这是我想要实现的目标:带有"x"的小错误框组件.消除它.通过其道具接收到错误将显示该错误,但是我想一种从其自己的代码中关闭该错误的方法.

I know this question has been asked a couple of times already but most of the time, the solution is to handle this in the parent, as the flow of responsibility is only descending. However, sometimes, you need to kill a component from one of its methods.I know I can't modify its props, and If I start adding booleans as the state, it's gonna start to be really messy for a simple component. Here is what I'm trying to achieve :A small error box component, with an "x" to dismiss it. Receiving an error through its props will display it but I'd like a way to close it from its own code.

class ErrorBoxComponent extends React.Component {

  dismiss() {
    // What should I put here?
  }
  
  render() {
    if (!this.props.error) {
      return null;
    }

    return (
      <div data-alert className="alert-box error-box">
        {this.props.error}
        <a href="#" className="close" onClick={this.dismiss.bind(this)}>&times;</a>
      </div>
    );
  }
}


export default ErrorBoxComponent;

我会在父组件中这样使用它:

And I would use it like this in the parent component :

<ErrorBox error={this.state.error}/>

我应该在此处放置什么?部分中,我已经尝试过:

In the section What should I put here ?, I already tried :

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);这会在控制台中引发一个不错的错误:

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);Which throws a nice error in the console :

我应该将传入的道具复制到ErrorBox状态,并仅在内部对其进行操作吗?

Should I copy the incoming props in the ErrorBox state, and manipulate it only internally?

推荐答案

就像您收到的警告一样,您正在尝试做React中的反模式.这是不行的. React旨在使父母与孩子之间的关系不复存在.现在,如果您希望孩子自行卸载,则可以使用由孩子触发的父状态更改来模拟此情况.让我向您展示代码.

Just like that nice warning you got, you are trying to do something that is an Anti-Pattern in React. This is a no-no. React is intended to have an unmount happen from a parent to child relationship. Now if you want a child to unmount itself, you can simulate this with a state change in the parent that is triggered by the child. let me show you in code.

class Child extends React.Component {
    constructor(){}
    dismiss() {
        this.props.unmountMe();
    } 
    render(){
        // code
    }
}

class Parent ...
    constructor(){
        super(props)
        this.state = {renderChild: true};
        this.handleChildUnmount = this.handleChildUnmount.bind(this);
    }
    handleChildUnmount(){
        this.setState({renderChild: false});
    }
    render(){
        // code
        {this.state.renderChild ? <Child unmountMe={this.handleChildUnmount} /> : null}
    }

}

这是一个非常简单的示例.但您可以看到一种粗略的方法来将动作传递给父项

this is a very simple example. but you can see a rough way to pass through to the parent an action

话虽这么说,您可能应该在商店中进行检查(调度操作),以使商店在进行渲染时能够包含正确的数据

That being said you should probably be going through the store (dispatch action) to allow your store to contain the correct data when it goes to render

我已经为两个单独的应用程序完成了错误/状态消息,它们都通过了商店.这是首选方法...如果您愿意,我可以发布一些有关如何执行此操作的代码.

I've done error/status messages for two separate applications, both went through the store. It's the preferred method... If you'd like I can post some code as to how to do that.

首先要注意的几件事.这是在打字稿中,因此您需要删除类型声明:)

Few things to note first. this is in typescript so you would need to remove the type declarations :)

我正在使用npm软件包lodash进行操作,并使用类名(cx别名)进行内联类名分配.

I am using the npm packages lodash for operations, and classnames (cx alias) for inline classname assignment.

此设置的好处是,在操作创建通知时,我为每个通知使用唯一的标识符. (例如notify_id).此唯一ID是Symbol().这样,如果您想随时删除任何通知,就可以,因为您知道要删除的通知.此通知系统将使您可以堆叠任意数量的对象,动画完成后它们将消失.我迷上了动画事件,当它完成时,我触发了一些代码来删除通知.我还设置了一个后备超时以删除通知,以防动画回调不触发.

The beauty of this setup is I use a unique identifier for each notification when the action creates it. (e.g. notify_id). This unique ID is a Symbol(). This way if you want to remove any notification at any point in time you can because you know which one to remove. This notification system will let you stack as many as you want and they will go away when the animation is completed. I am hooking into the animation event and when it finishes I trigger some code to remove the notification. I also set up a fallback timeout to remove the notification just in case the animation callback doesn't fire.

import { USER_SYSTEM_NOTIFICATION } from '../constants/action-types';

interface IDispatchType {
    type: string;
    payload?: any;
    remove?: Symbol;
}

export const notifySuccess = (message: any, duration?: number) => {
    return (dispatch: Function) => {
        dispatch({ type: USER_SYSTEM_NOTIFICATION, payload: { isSuccess: true, message, notify_id: Symbol(), duration } } as IDispatchType);
    };
};

export const notifyFailure = (message: any, duration?: number) => {
    return (dispatch: Function) => {
        dispatch({ type: USER_SYSTEM_NOTIFICATION, payload: { isSuccess: false, message, notify_id: Symbol(), duration } } as IDispatchType);
    };
};

export const clearNotification = (notifyId: Symbol) => {
    return (dispatch: Function) => {
        dispatch({ type: USER_SYSTEM_NOTIFICATION, remove: notifyId } as IDispatchType);
    };
};

notification-reducer.ts

const defaultState = {
    userNotifications: []
};

export default (state: ISystemNotificationReducer = defaultState, action: IDispatchType) => {
    switch (action.type) {
        case USER_SYSTEM_NOTIFICATION:
            const list: ISystemNotification[] = _.clone(state.userNotifications) || [];
            if (_.has(action, 'remove')) {
                const key = parseInt(_.findKey(list, (n: ISystemNotification) => n.notify_id === action.remove));
                if (key) {
                    // mutate list and remove the specified item
                    list.splice(key, 1);
                }
            } else {
                list.push(action.payload);
            }
            return _.assign({}, state, { userNotifications: list });
    }
    return state;
};

app.tsx

在您的应用程序的基本渲染中,您将渲染通知

app.tsx

in the base render for your application you would render the notifications

render() {
    const { systemNotifications } = this.props;
    return (
        <div>
            <AppHeader />
            <div className="user-notify-wrap">
                { _.get(systemNotifications, 'userNotifications') && Boolean(_.get(systemNotifications, 'userNotifications.length'))
                    ? _.reverse(_.map(_.get(systemNotifications, 'userNotifications', []), (n, i) => <UserNotification key={i} data={n} clearNotification={this.props.actions.clearNotification} />))
                    : null
                }
            </div>
            <div className="content">
                {this.props.children}
            </div>
        </div>
    );
}

user-notification.tsx

用户通知类

/*
    Simple notification class.

    Usage:
        <SomeComponent notifySuccess={this.props.notifySuccess} notifyFailure={this.props.notifyFailure} />
        these two functions are actions and should be props when the component is connect()ed

    call it with either a string or components. optional param of how long to display it (defaults to 5 seconds)
        this.props.notifySuccess('it Works!!!', 2);
        this.props.notifySuccess(<SomeComponentHere />, 15);
        this.props.notifyFailure(<div>You dun goofed</div>);

*/

interface IUserNotifyProps {
    data: any;
    clearNotification(notifyID: symbol): any;
}

export default class UserNotify extends React.Component<IUserNotifyProps, {}> {
    public notifyRef = null;
    private timeout = null;

    componentDidMount() {
        const duration: number = _.get(this.props, 'data.duration', '');
       
        this.notifyRef.style.animationDuration = duration ? `${duration}s` : '5s';

        
        // fallback incase the animation event doesn't fire
        const timeoutDuration = (duration * 1000) + 500;
        this.timeout = setTimeout(() => {
            this.notifyRef.classList.add('hidden');
            this.props.clearNotification(_.get(this.props, 'data.notify_id') as symbol);
        }, timeoutDuration);

        TransitionEvents.addEndEventListener(
            this.notifyRef,
            this.onAmimationComplete
        );
    }
    componentWillUnmount() {
        clearTimeout(this.timeout);

        TransitionEvents.removeEndEventListener(
            this.notifyRef,
            this.onAmimationComplete
        );
    }
    onAmimationComplete = (e) => {
        if (_.get(e, 'animationName') === 'fadeInAndOut') {
            this.props.clearNotification(_.get(this.props, 'data.notify_id') as symbol);
        }
    }
    handleCloseClick = (e) => {
        e.preventDefault();
        this.props.clearNotification(_.get(this.props, 'data.notify_id') as symbol);
    }
    assignNotifyRef = target => this.notifyRef = target;
    render() {
        const {data, clearNotification} = this.props;
        return (
            <div ref={this.assignNotifyRef} className={cx('user-notification fade-in-out', {success: data.isSuccess, failure: !data.isSuccess})}>
                {!_.isString(data.message) ? data.message : <h3>{data.message}</h3>}
                <div className="close-message" onClick={this.handleCloseClick}>+</div>
            </div>
        );
    }
}

这篇关于如何在React/Redux/Typescript通知消息中从自身上卸下,取消渲染或删除组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-24 15:25