Подтвердить что ты не робот

Как отключить, удалить или удалить компонент из себя в уведомлении React/Redux/Typescript

Я знаю, что этот вопрос задавали пару раз уже, но большую часть времени, решение должно справиться с этим в родительском, поскольку поток ответственности только спускается. Однако иногда вам нужно убить компонент из одного из его методов. Я знаю, что не могу изменить его реквизит, и если я начну добавлять булевы как состояние, это станет действительно грязным для простого компонента. Вот что я пытаюсь достичь: Небольшой компонент окна ошибок с "x", чтобы отклонить его. Получение ошибки через его реквизиты покажет ее, но я хотел бы, чтобы закрыть ее из собственного кода.

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;

И я бы использовал его как в родительском компоненте:

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

В разделе Что мне делать?, я уже пробовал:

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode); Что вызывает приятную ошибку в консоли:

Предупреждение: unmountComponentAtNode(): node, который вы пытаетесь размонтировать, был отображен React и не является контейнером верхнего уровня. Вместо этого родительский компонент обновит свое состояние и rerender, чтобы удалить этот компонент.

Должен ли я копировать входящие реквизиты в состояние ErrorBox и управлять им только внутри?

4b9b3361

Ответ 1

Просто как это хорошее предупреждение, которое вы получили, вы пытаетесь сделать что-то, что является Anti-Pattern in React. Это нет. Реагирование предназначено для того, чтобы произойти отключение от отношения родителя к ребенку. Теперь, если вы хотите, чтобы ребенок размонтировал себя, вы можете имитировать это с изменением состояния родителя, который запускается дочерним элементом. позвольте мне показать вам код.

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}
    }

}

Это очень простой пример. но вы можете увидеть грубый способ передать родителю действие

При этом вам, вероятно, следует пройти через магазин (диспетчерское действие), чтобы ваш магазин мог содержать правильные данные, когда он идет для рендеринга

Я сделал сообщения об ошибке/статусе для двух отдельных приложений, оба прошли через хранилище. Его предпочтительный метод... Если вы хотите, я могу опубликовать некоторый код о том, как это сделать.

EDIT: Вот как я настраиваю систему уведомлений с использованием React/Redux/Typescript

Несколько вещей, которые нужно отметить первым.. это находится в typescript, поэтому вам нужно будет удалить объявления типа:)

Я использую пакеты lpm для npm для операций и classnames (cx alias) для присвоения имени inline classname.

Красота этой установки заключается в том, что я использую уникальный идентификатор для каждого уведомления, когда действие создает его. (например, notify_id). Этот уникальный идентификатор - Symbol(). Таким образом, если вы хотите удалить любое уведомление в любой момент времени, вы можете, потому что знаете, какой из них удалить. Эта система уведомлений позволит вам складывать столько, сколько вы захотите, и они исчезнут, когда анимация будет завершена. Я подключаюсь к событию анимации, и когда он заканчивается, я запускаю код для удаления уведомления. Я также устанавливаю резервный тайм-аут, чтобы удалить уведомление, просто не получив обратного вызова анимации.

уведомление-actions.ts

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);
    };
};

уведомление-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

в базовом рендере для вашего приложения вы будете отображать уведомления

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>
    );
}

пользователя 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>
        );
    }
}

Ответ 2

вместо

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);

попробуйте использовать

ReactDOM.unmountComponentAtNode(document.getElementById('root'));

Ответ 3

В большинстве случаев достаточно просто скрыть элемент, например, таким образом:

export default class ErrorBoxComponent extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            isHidden: false
        }
    }

    dismiss() {
        this.setState({
            isHidden: true
        })
    }

    render() {
        if (!this.props.error) {
            return null;
        }

        return (
            <div data-alert className={ "alert-box error-box " + (this.state.isHidden ? 'DISPLAY-NONE-CLASS' : '') }>
                { this.props.error }
                <a href="#" className="close" onClick={ this.dismiss.bind(this) }>&times;</a>
            </div>
        );
    }
}

Или вы можете сделать рендер/рендер/не рендеринг через родительский компонент следующим образом

export default class ParentComponent extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            isErrorShown: true
        }
    }

    dismiss() {
        this.setState({
            isErrorShown: false
        })
    }

    showError() {
        if (this.state.isErrorShown) {
            return <ErrorBox 
                error={ this.state.error }
                dismiss={ this.dismiss.bind(this) }
            />
        }

        return null;
    }

    render() {

        return (
            <div>
                { this.showError() }
            </div>
        );
    }
}

export default class ErrorBoxComponent extends React.Component {
    dismiss() {
        this.props.dismiss();
    }

    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>
        );
    }
}

Наконец, есть способ удалить html node, но я действительно не знаю, это хорошая идея. Может быть, кто-то, кто знает Реакт от внутреннего, что-то скажет об этом.

export default class ErrorBoxComponent extends React.Component {
    dismiss() {
        this.el.remove();
    }

    render() {
        if (!this.props.error) {
            return null;
        }

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

Ответ 4

Я был на этом посту около 10 раз и просто хотел оставить здесь свои два цента. Вы можете просто размонтировать его условно.

if (renderMyComponent) {
  <MyComponent props={...} />
}

Все, что вам нужно сделать, это удалить его из DOM, чтобы размонтировать его.

Пока renderMyComponent = true, компонент будет отображаться. Если вы установите renderMyComponent = false, он будет размонтирован из DOM.