我知道这个问题已经被问过几次了,但是在大多数情况下,解决方法是在父级中解决这个问题,因为责任流只是在下降。但是,有时您需要使用一种方法杀死组件。我知道我无法修改其道具,并且如果我开始添加布尔值作为状态,那么对于一个简单的组件而言,它将会变得非常混乱。这是我要实现的目标:一个小的错误框组件,带有一个“ 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)}>×</a> </div> ); } } export default ErrorBoxComponent;
我会在父组件中这样使用它:
<ErrorBox error={this.state.error}/>
在本节中 我应该在这里放什么? ,我已经尝试过:
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode); 这在控制台中引发了一个不错的错误:
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);
警告:unmountComponentAtNode():您尝试卸载的节点是由React渲染的,不是顶级容器。而是让父组件更新其状态并重新渲染,以删除此组件。
我是否应该在ErrorBox状态下复制传入的道具,并仅在内部对其进行操作?
就像您收到的警告一样,您正在尝试做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} } }
这是一个非常简单的例子。但您可以看到一种粗略的方法来将动作传递给父项
话虽这么说,您可能应该遍历商店(调度操作)以允许商店在呈现时包含正确的数据
我已经为两个单独的应用程序完成了错误/状态消息,它们都通过了商店。这是首选方法…如果您愿意,我可以发布一些有关如何执行此操作的代码。
首先要注意的事情。这是在打字稿中,所以您需要删除类型声明:)
我正在使用npm包lodash进行操作,并使用类名(cx别名)进行内联类名分配。
此设置的好处是,在操作创建通知时,我为每个通知使用唯一的标识符。(例如notify_id)。此唯一ID是Symbol()。这样,如果您想随时删除任何通知,就可以,因为您知道要删除的通知。此通知系统将使您可以堆叠任意数量的对象,动画完成后它们将消失。我迷上了动画事件,完成后我触发了一些代码来删除通知。我还设置了一个后备超时以删除通知,以防动画回调不触发。
Symbol()
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); }; };
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; };
在应用程序的基本渲染中,您将渲染通知
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> ); }
用户通知类
/* 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> ); } }