[解決済み] React/Redux/Typescriptの通知メッセージでコンポーネントをアンマウント、アンレンダリング、または削除する方法
質問
この質問はすでに何度かされていると思いますが、ほとんどの場合、責任の流れが下降するのみなので、解決策は親でこれを処理することです。しかし、時にはコンポーネントをそのメソッドの1つから削除しなければならないことがあります。 プロップを変更できないことは分かっていますし、ステートとしてブール値を追加し始めると、単純なコンポーネントとしては本当に厄介なことになりそうです。以下は、私が達成しようとしていることです。 小さなエラーボックスコンポーネントで、"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);
これはコンソールに素晴らしいエラーを投げます。
警告: unmountComponentAtNode(): アンマウントしようとしているノードは React によってレンダリングされたもので、トップレベルのコンテナではありません。代わりに、このコンポーネントを削除するために、親コンポーネントにその状態を更新させ、再レンダリングさせてください。
ErrorBoxの状態で受信したpropsをコピーし、内部でのみ操作すればよいのでしょうか?
どのように解決するのですか?
警告が出たように、あなたは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}
}
}
これは非常に単純な例ですが、親にアクションを渡すための大まかな方法を見ることができます。
とはいえ、レンダリングするときに正しいデータを格納できるように、ストア (ディスパッチアクション) を通過させるべきでしょう。
私は 2 つの別々のアプリケーションでエラー/ステータス メッセージを作成しましたが、両方ともストアを経由していました。これは望ましい方法です... 必要であれば、それを行う方法についていくつかのコードを投稿することができます。
EDIT: React/Redux/Typescript を使って通知システムをセットアップする方法を紹介します。
これはtypescriptで書かれているので、型宣言を削除する必要があります :)
操作にはnpmパッケージのlodash、インラインでのクラス名付与にはclassnames(cx alias)を使用しています。
このセットアップの美点は、アクションがそれを作成するとき、それぞれの通知に対して一意な識別子を使うことです。(例: notify_id) です。このユニークなIDは
Symbol()
. この方法で、もしあなたが任意の時点で任意の通知を削除したい場合、あなたはどの通知を削除するべきか知っているので、できます。この通知システムは、好きなだけ通知を積み重ねることができ、アニメーションが完了すると消えていきます。私は、アニメーション・イベントにフックして、それが終了したときに、通知を削除するコードをトリガーしている。また、アニメーションのコールバックが発射されない場合に備えて、通知を削除するためのフォールバックタイムアウトを設定しました。
通知アクション.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);
};
};
通知-減速機.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>
);
}
}
関連
-
[解決済み】ReactJS: マテリアルuiのブレークポイントについて
-
[解決済み] テスト
-
[解決済み] react - createMuiThemeとcreateThemeの違い。
-
[解決済み] useStateプロパティのフックにmap関数を使用する方法
-
[解決済み] Angular 2の*ngForのReactでの同等品
-
[解決済み] React JSでは、状態を直接変異させない、setState() react/no-direct-mutation-stateを使用する。
-
[解決済み] axios-mock-adapter onGet mock data not effective.
-
[解決済み] React Router - バージョン更新後のwithRouterでTypescriptエラーが発生する。
-
[解決済み] Reactのrender()にFont Awesomeのアイコンを入れる方法
-
[解決済み] クエリ文字列からパラメータ値を取得する方法は?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】React 17で動作するEnzymeアダプターはどれですか?
-
[解決済み] マテリアルUIセレクトフィールドのマルチセレクト
-
[解決済み] configuration.module に未知のプロパティ 'loaders' があります。
-
[解決済み] ReactJsのCreateClassは関数ではない
-
[解決済み] リアクトです。<tr>は<td>の子として表示できません。コメント > td > tr を参照してください。
-
[解決済み] React JSでは、状態を直接変異させない、setState() react/no-direct-mutation-stateを使用する。
-
[解決済み] ReactJS で inst.render が関数でないエラーが発生する
-
[解決済み] eslint: no-case-declaration - case ブロックで予期しない字句の宣言があった。
-
[解決済み] React」は定義される前に使用されていた
-
[解決済み] Reactのrender()にFont Awesomeのアイコンを入れる方法