programing

React/Redux/Typescript 알림 메시지에서 컴포넌트를 자체에서 마운트 해제, 렌더 해제 또는 삭제하는 방법

telebox 2023. 3. 28. 21:35
반응형

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

"What I close here?" 섹션에서 이미 시도했습니다.

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);그러면 콘솔에 다음과 같은 오류가 발생합니다.

경고: unmount ComponentAtNode():마운트 해제하려는 노드는 React에서 렌더링한 것으로 최상위 컨테이너가 아닙니다.대신 부모 컴포넌트의 상태를 갱신하고 이 컴포넌트를 삭제하도록 합니다.

착신 소품을 ErrorBox 상태로 복사하여 내부에서만 조작해야 합니까?

당신이 받은 좋은 경고와 마찬가지로 당신은 Anti-Pattern in React라는 것을 하려고 하고 있다.이건 안 돼.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}
    }

}

이것은 매우 간단한 예시입니다. 하지만 당신은 부모에게 전달되는 대략적인 방법을 볼 수 있습니다.

스토어가 렌더링할 때 올바른 데이터를 포함할 수 있도록 스토어(디스패치 액션)를 거쳐야 합니다.

두 개의 다른 애플리케이션에 대해 오류/상태 메시지를 실행했는데, 둘 다 스토어를 통해 확인되었습니다.그게 선호되는 방법인데...원하시면 어떻게 하는지 코드를 올릴 수 있습니다.

편집: React/Redux/Typescript를 사용하여 알림 시스템을 설정하는 방법은 다음과 같습니다.

먼저 주의할 사항이 몇 가지 있습니다.이것은 typescript에 기재되어 있기 때문에 type declarations를 삭제해야 합니다.

작업에는 npm 패키지 lodash를 사용하고 인라인 클래스명 할당에는 클래스명(cx 에일리어스)을 사용하고 있습니다.

이 설정의 장점은 작업을 통해 알림이 생성될 때 알림마다 고유 식별자를 사용하는 것입니다(예: notify_id).는 ''입니다.Symbol()이렇게 하면 제거할 알림을 알 수 있으므로 언제든지 알림을 제거할 수 있습니다.이 알림 시스템을 사용하면 원하는 만큼 쌓을 수 있으며 애니메이션이 완료되면 사라집니다.애니메이션 이벤트에 접속하고 있으며, 이벤트가 종료되면 몇 가지 코드를 트리거하여 알림을 제거합니다.애니메이션 콜백이 실행되지 않을 경우에 대비하여 알림을 삭제하는 폴백 타임아웃도 설정했습니다.

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

notification-secer.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>
    );
}

사용자 알림.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>
        );
    }
}

사용하는 대신

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

써보다

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

대부분의 경우 다음과 같이 요소를 숨기는 것만으로 충분합니다.

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 노드를 제거하는 방법이 있는데, 정말 좋은 생각인지 모르겠어요.내부로부터 리액트를 아는 누군가가 이것에 대해 뭐라고 말할지도 모른다.

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

나는 지금 이 게시물을 10번 정도 방문했고 내 의견을 여기에 남겨두고 싶었다.조건부로 마운트 해제할 수 있습니다.

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

마운트 해제하려면 DOM에서 삭제하기만 하면 됩니다.

renderMyComponent = true컴포넌트가 렌더링 됩니다.「 」를 설정했을 renderMyComponent = falseDOM dom dom 、 DOM dom 。

이 모든 것은 로 할 수 .return false특정 기준이 충족되거나 충족되지 않는 경우 구성요소 자체 내에 있습니다.

구성 요소를 마운트 해제하지는 않지만 렌더링된 모든 콘텐츠를 제거합니다.컴포넌트에 이벤트청취자가 포함되어 있으면 컴포넌트가 필요 없게 되었을 때 삭제해야 합니다.

import React, { Component } from 'react';

export default class MyComponent extends Component {
    constructor(props) {
        super(props);

        this.state = {
            hideComponent: false
        }
    }

    closeThis = () => {
        this.setState(prevState => ({
            hideComponent: !prevState.hideComponent
        })
    });

    render() {
        if (this.state.hideComponent === true) {return false;}

        return (
            <div className={`content`} onClick={() => this.closeThis}>
                YOUR CODE HERE
            </div>
        );
    }
}

언급URL : https://stackoverflow.com/questions/36985738/how-to-unmount-unrender-or-remove-a-component-from-itself-in-a-react-redux-typ

반응형