reactjs unmount

reactjs - Cómo desmontar, deshacer o eliminar un componente de sí mismo en un mensaje de notificación React/Redux/Typecript



unmount (4)

Sé que esta pregunta ya se ha hecho un par de veces, pero la mayoría de las veces, la solución es manejar esto en los padres, ya que el flujo de responsabilidad solo está descendiendo. Sin embargo, a veces, debe eliminar un componente de uno de sus métodos. Sé que no puedo modificar sus accesorios, y si empiezo a agregar booleanos como estado, comenzará a ser realmente complicado para un componente simple. Esto es lo que estoy tratando de lograr: un pequeño componente de cuadro de error, con una "x" para descartarlo. Recibir un error a través de sus accesorios lo mostrará, pero me gustaría una forma de cerrarlo desde su propio código.

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;

Y lo usaría así en el componente principal:

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

En la sección ¿Qué debo poner aquí? Ya lo intenté:

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode); Lo que arroja un buen error en la consola:

Advertencia: unmountComponentAtNode (): el nodo que está intentando desmontar fue procesado por React y no es un contenedor de nivel superior. En su lugar, haga que el componente principal actualice su estado y vuelva a procesar para eliminar este componente.

¿Debo copiar los accesorios entrantes en el estado ErrorBox y manipularlos solo internamente?


Al igual que esa agradable advertencia que recibió, está tratando de hacer algo que es un Anti-Pattern en React. Este es un no no. Reaccionar tiene la intención de que ocurra un desmontaje de una relación padre a hijo. Ahora, si desea que un niño se desmonte, puede simular esto con un cambio de estado en el padre que es activado por el niño. déjame mostrarte en código.

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

Este es un ejemplo muy simple. pero puedes ver una forma aproximada de pasarle a los padres una acción

Dicho esto, probablemente debería pasar por la tienda (acción de envío) para permitir que su tienda contenga los datos correctos cuando se procese

He hecho mensajes de error / estado para dos aplicaciones separadas, ambas pasaron por la tienda. Es el método preferido ... Si lo desea, puedo publicar un código sobre cómo hacerlo.

EDITAR: así es como configuro un sistema de notificación usando React / Redux / Typescript

Algunas cosas a tener en cuenta primero ... esto está en mecanografiado, por lo que deberá eliminar las declaraciones de tipo :)

Estoy usando los paquetes npm lodash para operaciones y los nombres de clase (alias cx) para la asignación de nombres de clase en línea.

La belleza de esta configuración es que uso un identificador único para cada notificación cuando la acción lo crea. (por ejemplo, notify_id). Esta identificación única es un Symbol() . De esta manera, si desea eliminar cualquier notificación en cualquier momento, puede saber cuál eliminar. Este sistema de notificación le permitirá apilar tantos como desee y desaparecerán cuando se complete la animación. Me estoy enganchando al evento de animación y cuando termina, disparo un código para eliminar la notificación. También configuré un tiempo de espera de reserva para eliminar la notificación en caso de que la devolución de llamada de animación no se active.

notificación-acciones.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); }; };

notificación-reductor.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

en el render base de su aplicación, usted representaría las notificaciones

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-notify.tsx

clase de notificación de usuario

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


En la mayoría de los casos, es suficiente con ocultar el elemento, por ejemplo de esta manera:

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

O puede renderizar / volver a procesar / no procesar a través de un componente principal como este

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

Finalmente, hay una manera de eliminar el nodo html, pero realmente no sé si es una buena idea. Quizás alguien que sepa Reaccionar internamente diga algo sobre esto.

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


En lugar de usar

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

intenta usar

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


He estado en esta publicación unas 10 veces y solo quería dejar mis dos centavos aquí. Puedes desmontarlo condicionalmente.

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

Todo lo que tienes que hacer es eliminarlo del DOM para desmontarlo.

Mientras renderMyComponent = true , el componente se renderizará. Si configura renderMyComponent = false , se desmontará del DOM.