React / Redux / Typescript bildirim mesajında ​​bir bileşenin kendisinden nasıl kaldırılır, işlenmez veya kaldırılır


114

Bu sorunun halihazırda birkaç kez sorulduğunu biliyorum ama çoğu zaman çözüm bunu ebeveynde halletmektir, çünkü sorumluluk akışı sadece azalıyor. Ancak bazen, bir bileşeni yöntemlerinden birinden öldürmeniz gerekir. Props'larını değiştiremeyeceğimi biliyorum ve eğer durum olarak boole'leri eklemeye başlarsam, basit bir bileşen için gerçekten karmaşık olmaya başlayacak. İşte başarmaya çalıştığım şey: Küçük bir hata kutusu bileşeni, onu yok saymak için bir "x" ile. Props aracılığıyla bir hata aldığınızda bunu gösterecek, ancak kendi kodundan kapatmanın bir yolunu istiyorum.

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;

Ve bunu ana bileşende şu şekilde kullanırdım:

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

Bölümde Burada ne yazmalıyım? , Zaten denedim:

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode); Konsolda güzel bir hata veren:

Uyarı: unmountComponentAtNode (): UnmountComponentAtNode (): Bağlantısını kesmeye çalıştığınız düğüm, React tarafından oluşturuldu ve üst düzey bir konteyner değil. Bunun yerine, bu bileşeni kaldırmak için üst bileşenin durumunu güncellemesini ve yeniden oluşturmasını sağlayın.

Gelen propları ErrorBox durumunda kopyalayıp sadece dahili olarak mı değiştirmeliyim?


Redux kullanıyor musunuz?
Arnau Lacambra

Bu neden bir gerekliliktir "Aksesuarlarından bir hata almak onu görüntüleyecek ama ben kendi kodundan kapatmanın bir yolunu istiyorum."? Normal yaklaşım, hata durumunu ortadan kaldıracak bir eylem göndermek ve ardından sizin ima ettiğiniz gibi ebeveynin bir oluşturma döngüsünde kapatılması olacaktır.
ken4z

Aslında her ikisi için de olasılık sunmak istiyorum. Aslında, açıkladığınız gibi kapatılabilir, ama benim
durumum

Yanıtlar:


97

Tıpkı aldığınız güzel uyarı gibi, React'te Anti-Pattern olan bir şey yapmaya çalışıyorsunuz. Bu bir hayır-hayır. React, bir ebeveynden çocuğa bir ilişkinin kesilmesi için tasarlanmıştır. Şimdi, bir çocuğun kendi başına bağlantısını kesmesini istiyorsanız, bunu çocuk tarafından tetiklenen ebeveynde bir durum değişikliği ile simüle edebilirsiniz. size kodla göstermeme izin verin.

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

}

bu çok basit bir örnek. ancak ebeveyne bir eylemi aktarmanın kaba bir yolunu görebilirsiniz

Bununla birlikte, mağazanızın oluşturulmaya gittiğinde doğru verileri içermesine izin vermek için muhtemelen mağazadan (gönderim eylemi) geçmeniz gerektiği söyleniyor.

İki ayrı uygulama için hata / durum mesajları yaptım, ikisi de mağazadan geçti. Tercih edilen yöntem bu ... İsterseniz, bunun nasıl yapılacağına dair bazı kodlar gönderebilirim.

DÜZENLEME: İşte React / Redux / Typescript kullanarak bir bildirim sistemini nasıl kurduğum

İlk önce not edilecek birkaç şey. bu typcript içindedir, bu yüzden tür bildirimlerini kaldırmanız gerekir :)

İşlemler için lodash npm paketlerini ve satır içi sınıf adı ataması için sınıf adlarını (cx diğer adı) kullanıyorum.

Bu kurulumun güzelliği, eylem onu ​​oluşturduğunda her bildirim için benzersiz bir tanımlayıcı kullanmamdır. (örneğin notify_id). Bu benzersiz kimlik birSymbol() . Bu şekilde, herhangi bir zamanda herhangi bir bildirimi kaldırmak isterseniz, hangisini kaldıracağınızı bildiğiniz için yapabilirsiniz. Bu bildirim sistemi, istediğiniz kadar istiflemenize izin verecek ve animasyon tamamlandığında ortadan kalkacaktır. Animasyon olayına bağlanıyorum ve bittiğinde bildirimi kaldırmak için bazı kodları tetikliyorum. Ayrıca, animasyon geri aramasının tetiklenmemesi durumunda, bildirimi kaldırmak için bir geri dönüş zaman aşımı ayarladım.

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

bildirim 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

uygulamanız için temel oluşturmada bildirimleri

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

kullanıcı notification.tsx

kullanıcı bildirim sınıfı

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

1
"mağazadan" mı? Sanırım, bununla ilgili birkaç önemli dersi kaçırıyorum: D Cevap ve kod için teşekkürler, ancak bunun basit bir hata mesajı görüntüleme bileşeni için cidden fazla olduğunu düşünmüyor musunuz? Çocuk hakkında tanımlanan bir eylemi yürütmek ebeveynin sorumluluğunda olmamalıdır ...
Sephy

Çocuğu DOM'a yerleştirmekten ebeveyn sorumlu olduğu için aslında ebeveyn olmalıdır. Dediğim gibi, bunu yapmanın bir yolu olsa da tavsiye etmem. Mağazanızı güncelleyen bir eylem kullanıyor olmalısınız. hem Flux hem de Redux modelleri bu şekilde kullanılmalıdır.
John Ruddell

Tamam o zaman, lütfen bir miktar kod parçacığı almaktan memnuniyet duyarım. Flux ve Reduc hakkında biraz okuduğumda o kod parçasına geri döneceğim!
Sephy

Tamam evet sanırım bunu yapmanın bir yolunu gösteren basit bir github deposu yapacağım. Son yaptığım css animasyonlarını kullanarak dize veya html öğelerini işleyebilen öğeyi soldurdum ve ardından animasyon tamamlandığında bunu dinlemek için javascript kullandım ve ardından kendi kendini temizledim (DOM'dan kaldır) animasyon bitti veya kapat düğmesine tıkladınız.
John Ruddell

Lütfen yapın, React'in felsefesini kavramakta biraz mücadele eden benim gibi başkalarına yardımcı olabilirse. Ayrıca, bunun için bir git deposu koyarsanız, geçen süre için puanlarımdan biraz ayrılmaktan memnuniyet duyarım! Yüz puan diyelim (ödül 2 gün içinde alınabilir)
Sephy

25

kullanmak yerine

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

kullanmayı dene

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

Bunu React 15 ile deneyen oldu mu? Bu hem potansiyel olarak yararlı hem de muhtemelen bir anti-model gibi görünüyor.
theUtherSide

4
@theUtherSide bu, tepkimede bir anti modeldir. React belgeleri, bir çocuğu ebeveynden state / props aracılığıyla ayırmanızı tavsiye ediyor
John Ruddell

1
Ya kaldırılan bileşen React uygulamanızın köküse, ancak değiştirilen kök öğe değilse? Örneğin <div id="c1"><div id="c2"><div id="react-root" /></div></div>. Ya iç metni c1değiştirilirse?
flipdoubt

1
Özellikle react olmayan bir uygulamada bulunan bir react uygulamanız varsa, kök bileşeninizin bağlantısını kesmek istiyorsanız bu yararlıdır. Bunu kullanmak zorunda kaldım çünkü başka bir uygulama tarafından yönetilen bir modal içinde react oluşturmak istedim ve modal'ında modu gizleyecek kapatma düğmeleri var, ancak reactdomum hala bağlı kalacak. reactjs.org/blog/2015/10/01/react-render-and-top-level-api.html
Abba

10

Çoğu durumda, yalnızca öğeyi gizlemek yeterlidir, örneğin şu şekilde:

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

Veya bunun gibi üst bileşen aracılığıyla oluşturabilir / yeniden oluşturabilir / oluşturmayabilirsiniz

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

Son olarak, html düğümünü kaldırmanın bir yolu var, ancak bunun iyi bir fikir olup olmadığını gerçekten bilmiyorum. Belki React'i içeriden tanıyan biri bu konuda bir şeyler söyleyecektir.

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

Ancak, çocuk listesindeki bir çocuğun bağlantısını kesmek istediğimde ... Klonlanmış bir bileşeni bu listedeki aynı anahtarla değiştirmek istersem ne yapabilirim?
roadev

1
anladığım kadarıyla böyle bir şey yapmak istiyorsunuz: document.getElementById (CHILD_NODE_ID) -> .remove (); -> document.getElementById (PARENT_NODE_ID) -> .appendChild (NEW_NODE)? Haklı mıyım Unut gitsin. Tepki yaklaşımı DEĞİLDİR. Koşul oluşturma için bileşen durumunu kullan
Sasha Kos

2

Bu yazıya yaklaşık 10 kez gittim ve sadece iki sentimi burada bırakmak istedim. Sadece koşullu olarak kaldırabilirsiniz.

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

Tek yapmanız gereken, bağlantısını kesmek için DOM'dan kaldırmaktır.

Olduğu sürece renderMyComponent = truebileşen işlenecektir. Eğer ayarlarsanız renderMyComponent = false, DOM'dan ayrılacaktır.


-1

Bu her durumda uygun değildir, ancak return falsebelirli bir kriter karşılanırsa veya karşılanmazsa , koşullu olarak bileşenin içinde olabilirsiniz .

Bileşeni ayırmaz, ancak oluşturulan tüm içeriği kaldırır. Bu, bence, bileşende artık ihtiyaç kalmadığında kaldırılması gereken olay dinleyicileriniz varsa kötü olur.

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>
        );
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.