web-dev-qa-db-ja.com

ReactJSでコンポーネントがアンマウントされたときにプロミスをキャンセルする

マウントされたプロミスを作成して呼び出す「アイテム」という名前のコンポーネントがあります。

class Item extends React.Component{
    constructor(props){
        super(props)
        this.onClick = this.onClick.bind(this)

        this.prom = new Promise((resolve, reject) => {
            setTimeout(() => resolve("PROMISE COMPLETED "+this.props.id),6000)
        })
    }

    componentDidMount(){
        this.prom.then((success) => {
            console.log(success)
        })
    }

    componentWillUnmount(){
       console.log("unmounted")
    }

    onClick(e){
        e.preventDefault()
        this.props.remove(this.props.id)
    }

    render(){
        return (
            <h1>Item {this.props.id} - <a href="#" onClick={this.onClick}>Remove</a></h1>
        )
    }
}

ご覧のとおり、promiseはresolveが呼び出されてから6秒後にそれを呼び出します。

画面にそれらの項目を表示することを担当する「リスト」という名前の別のコンポーネントがあります。 「リスト」は「アイテム」コンポーネントの親です。

class List extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            items : [1,2,3]
        }

        this.handleRemove = this.handleRemove.bind(this)
    }

    handleRemove(id){
        this.setState((prevState, props) => ({
            items : prevState.items.filter((cId) => cId != id)
        }));
    }

    render(){
        return (
            <div>
            {this.state.items.map((item) => (
                <Item key={item} id={item} remove={this.handleRemove}  />
            ))
            }
            </div>
        )
    }
}

ReactDOM.render(<List />,root)

上記の例では、画面に3つのアイテムが表示されています。

enter image description here

これらのコンポーネントのいずれかを削除すると、componentWillUnmount()が呼び出されますが、削除されたコンポーネントで作成されたpromiseも実行されます。

たとえば、2番目のアイテムを削除しても、2番目のアイテムのプロミスが実行されることがわかります。

unmounted 
PROMISE COMPLETED 1 
PROMISE COMPLETED 2 
PROMISE COMPLETED 3

コンポーネントがアンマウントされると、約束をキャンセルする必要があります。

14
amone

これのバリエーション https://hshno.de/BJ46Xb_r7 は私にはうまくいったようです。 mountedインスタンス変数を使用してHOCを作成し、すべての非同期コンポーネントをその中にラップしました。

以下は私のコードが大体好きなものです。

export function makeMountAware(Component) {
    return class MountAwareComponent extends React.Component {
        mounted = false;
        componentDidMount() {
            this.mounted = true;
        }
        componentWillUnmount() {
            this.mounted = false;
        }
        return (
            <Component 
                mounted = {this.mounted}
                {...this.props}
                {...this.state}
            />
        );
    }
}

class AsyncComponent extends React.Component {
    componentDidMount() {
        fetchAsyncData()
            .then(data => {
                this.props.mounted && this.setState(prevState => ({
                    ...prevState,
                    data
                }));
            });
    }
}
export default makeMountAware(AsyncComponent);
3
Rohan Bagchi

ES6のネイティブプロミスをキャンセルすることはできません。詳しくは https://medium.com/@benlesh/promise-cancellation-is-dead-long-live-promise-cancellation-c6601f1f5082 をご覧ください

しかし、あなたができることは、多分 Bluebird[〜#〜] q [〜#〜] のようなネイティブでないプロミスライブラリを使用することです。キャンセルされます。

1

できることはたくさんあります。最も簡単なのは、約束をrejectすることです。

this.prom = new Promise((resolve, reject) => {
     this.rejectProm = reject;
     ...
});

その後

componentWillUnmount(){
   if (this.rejectProm) {
      this.rejectProm();
      this.rejectProm = nil;
   }

   console.log("unmounted")
}
1
Sulthan

この例ではタイムアウトを使用しているため、アンマウントするときにタイムアウトをクリアする必要があります。

class Item extends React.Component{
    constructor(props){
        super(props)
        this.onClick = this.onClick.bind(this)

        // attribute for the timeout
        this.timeout = null;

        this.prom = new Promise((resolve, reject) => {
          // assign timeout
          this.timeout = setTimeout(() => resolve("PROMISE COMPLETED "+this.props.id),6000)
        })
    }

    componentDidMount(){
        this.prom.then((success) => {
            console.log(success)
        })
    }

    componentWillUnmount(){
       // clear timeout
       clearTimeout(this.timeout);
       console.log("unmounted")
    }

私の推測では、これは拒否され、コンソールログは表示されません。

0
valem