কীভাবে কম্পোনেন্টউইলমઉન્ટটিতে একটি আনয়ন বাতিল করবেন


90

আমার মনে হয় শিরোনামটি সব বলেছে। হলুদ সতর্কতাটি প্রতিবার প্রদর্শিত হয় যখনই আমি আনতে চলেছে এমন কোনও উপাদান আনমাউন্ট করি।

কনসোল

সতর্কতা: আনমাউন্ট থাকা উপাদানগুলিতে setState(বা forceUpdate) কল করতে পারবেন না । এটি একটি অন-অপশন, তবে ... componentWillUnmountপদ্ধতিতে সমস্ত সাবস্ক্রিপশন এবং অ্যাসিক্রোনাস টাস্কগুলি ঠিক করতে, বাতিল করতে ।

  constructor(props){
    super(props);
    this.state = {
      isLoading: true,
      dataSource: [{
        name: 'loading...',
        id: 'loading',
      }]
    }
  }

  componentDidMount(){
    return fetch('LINK HERE')
      .then((response) => response.json())
      .then((responseJson) => {
        this.setState({
          isLoading: false,
          dataSource: responseJson,
        }, function(){
        });
      })
      .catch((error) =>{
        console.error(error);
      });
  }

এটা কী হুঁশিয়ারি
দিচ্ছে আমার কাছে

প্রশ্ন আপডেট হয়েছে
জোওলো বেলো

আপনি কি প্রতিশ্রুতি দিয়েছিলেন বা আনার জন্য
অ্যাসিঙ্ক কোডটি করেছেন

আপনাকে কুইশনে কোড আনতে যোগ করুন
nima moradi

উত্তর:


80

আপনি যখন কোন প্রতিশ্রুতি দেন তখন সমাধান হওয়ার কয়েক সেকেন্ড সময় লাগতে পারে এবং ততক্ষণে ব্যবহারকারী আপনার অ্যাপ্লিকেশনটিতে অন্য জায়গায় নেভিগেট করতে পারে। সুতরাং যখন প্রতিশ্রুতি সমাধানগুলি setStateআনমাউন্ট করা উপাদানগুলিতে কার্যকর করা হয় এবং আপনি একটি ত্রুটি পান - ঠিক তেমন আপনার ক্ষেত্রে। এটি মেমরি ফাঁস হতে পারে।

এজন্য আপনার কিছু অ্যাসিনক্রোনাস যুক্তিকে উপাদানগুলির বাইরে সরিয়ে নেওয়া ভাল।

অন্যথায়, আপনি একরকম প্রয়োজন হবে আপনার প্রতিশ্রুতি বাতিল । বিকল্পভাবে - একটি সর্বশেষ অবলম্বন কৌশল হিসাবে (এটি একটি অ্যান্টিপ্যাটার্ন) - উপাদানটি এখনও মাউন্ট করা আছে কিনা তা পরীক্ষা করতে আপনি একটি পরিবর্তনশীল রাখতে পারেন:

componentDidMount(){
  this.mounted = true;

  this.props.fetchData().then((response) => {
    if(this.mounted) {
      this.setState({ data: response })
    }
  })
}

componentWillUnmount(){
  this.mounted = false;
}

আমি আবার জোর করব - এই এটি একটি প্রতিষেধক তবে এটি আপনার ক্ষেত্রে যথেষ্ট হতে পারে (ঠিক যেমন তারা Formikবাস্তবায়নের ক্ষেত্রেও করেছিল)।

গিটহাবের উপর একই রকম আলোচনা

সম্পাদনা:

হুক্সের সাথে আমি একই সমস্যাটি (প্রতিক্রিয়া ছাড়া কিছুই না থাকা) কীভাবে সমাধান করব :

বিকল্প একটি:

import React, { useState, useEffect } from "react";

export default function Page() {
  const value = usePromise("https://something.com/api/");
  return (
    <p>{value ? value : "fetching data..."}</p>
  );
}

function usePromise(url) {
  const [value, setState] = useState(null);

  useEffect(() => {
    let isMounted = true; // track whether component is mounted

    request.get(url)
      .then(result => {
        if (isMounted) {
          setState(result);
        }
      });

    return () => {
      // clean up
      isMounted = false;
    };
  }, []); // only on "didMount"

  return value;
}

বিকল্প বি: বিকল্পভাবে useRefযা শ্রেণীর স্থির সম্পত্তি হিসাবে আচরণ করে যার অর্থ এটি পরিবর্তিত হয় যখন উপাদানটির মূল্য পরিবর্তন হয় তখন এটি উপাদান পুনরায় সরবরাহ করে না:

function usePromise2(url) {
  const isMounted = React.useRef(true)
  const [value, setState] = useState(null);


  useEffect(() => {
    return () => {
      isMounted.current = false;
    };
  }, []);

  useEffect(() => {
    request.get(url)
      .then(result => {
        if (isMounted.current) {
          setState(result);
        }
      });
  }, []);

  return value;
}

// or extract it to custom hook:
function useIsMounted() {
  const isMounted = React.useRef(true)

  useEffect(() => {
    return () => {
      isMounted.current = false;
    };
  }, []);

  return isMounted; // returning "isMounted.current" wouldn't work because we would return unmutable primitive
}

উদাহরণ: https://codesandbox.io/s/86n1wq2z8


4
সুতরাং কম্পোনেন্ট উইলআউনমাউন্টে আনতে কেবল আসল উপায় নেই?
জোওলো বেলো

4
ওহ, আমি আপনার উত্তরের কোডটি আগে লক্ষ্য করিনি, এটি কাজ করেছিল। ধন্যবাদ
জোওলো বেলো


4
"এর জন্য আপনার অ্যাসিনক্রোনাস লজিককে উপাদানগুলির বাইরে নিয়ে যাওয়া ভাল?" এর অর্থ কী? প্রতিক্রিয়াযুক্ত সমস্ত কি উপাদান নয়?
কার্পিক

4
@ টমাসজ মুলারজার্জিক আপনাকে অনেক ধন্যবাদ, আপনি উপযুক্ত জিনিস দিয়েছিলেন।
কার্তিকিয়ান।

25

প্রতিক্রিয়ায় বন্ধুত্বপূর্ণ লোকেরা বাতিল করা প্রতিশ্রুতিতে আপনার ফেচ কল / প্রতিশ্রুতি মোড়ানোর পরামর্শ দেয়। কোডটি ক্লাস থেকে আলাদা রাখতে বা আনার সাথে ফাংশন রাখার জন্য সেই ডকুমেন্টেশনে কোনও প্রস্তাবনা নেই, তবে এটি যুক্তিযুক্ত বলে মনে হয় কারণ অন্যান্য শ্রেণি এবং ক্রিয়াকলাপগুলিতে এই কার্যকারিতাটির প্রয়োজন হওয়ার সম্ভাবনা রয়েছে, কোড ডুপ্লিকেশন একটি অ্যান্টি-প্যাটার্ন এবং নির্বিশেষে লম্বা কোড নিষ্পত্তি বা মধ্যে বাতিল করা উচিত componentWillUnmount()। প্রতিক্রিয়া অনুসারে, কোনও আনমাউন্টযুক্ত অংশে রাষ্ট্র নির্ধারণ এড়াতে cancel()আপনি মোড়ানো প্রতিশ্রুতিতে কল করতে পারেন componentWillUnmount

সরবরাহকৃত কোডগুলি এই কোড স্নিপেটগুলির মতো কিছু দেখায় যদি আমরা গাইড হিসাবে প্রতিক্রিয়া ব্যবহার করি:

const makeCancelable = (promise) => {
    let hasCanceled_ = false;

    const wrappedPromise = new Promise((resolve, reject) => {
        promise.then(
            val => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
            error => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
        );
    });

    return {
        promise: wrappedPromise,
        cancel() {
            hasCanceled_ = true;
        },
    };
};

const cancelablePromise = makeCancelable(fetch('LINK HERE'));

constructor(props){
    super(props);
    this.state = {
        isLoading: true,
        dataSource: [{
            name: 'loading...',
            id: 'loading',
        }]
    }
}

componentDidMount(){
    cancelablePromise.
        .then((response) => response.json())
        .then((responseJson) => {
            this.setState({
                isLoading: false,
                dataSource: responseJson,
            }, () => {

            });
        })
        .catch((error) =>{
            console.error(error);
        });
}

componentWillUnmount() {
    cancelablePromise.cancel();
}

---- সম্পাদনা করুন ----

আমি পেয়েছি উত্তরটি গিটহাবের সমস্যাটি অনুসরণ করে খুব সঠিক হতে পারে না। এখানে আমি ব্যবহার করি এটির একটি সংস্করণ যা আমার উদ্দেশ্যে কাজ করে:

export const makeCancelableFunction = (fn) => {
    let hasCanceled = false;

    return {
        promise: (val) => new Promise((resolve, reject) => {
            if (hasCanceled) {
                fn = null;
            } else {
                fn(val);
                resolve(val);
            }
        }),
        cancel() {
            hasCanceled = true;
        }
    };
};

ধারণাটি ছিল আবর্জনা সংগ্রাহকটিকে ফাংশনটি বা যা কিছু নালীর ব্যবহার করে মেমরি মুক্ত করতে সহায়তা করে।


গিথুব এ ইস্যুটির কি আপনার লিঙ্ক আছে
রেন

@ রেন, পৃষ্ঠা সম্পাদনা এবং সমস্যাগুলি নিয়ে আলোচনা করার জন্য একটি গিটহাব সাইট রয়েছে
haleonj

গিটিহাব প্রকল্পে সঠিক সমস্যাটি কোথায় আছে তা আমি আর নিশ্চিত নই।
haleonj

4
গিটহাব ইস্যুতে লিঙ্ক করুন: github.com/facebook/react/issues/5465
সামালফিক্স

22

আনার অনুরোধ বাতিল করতে আপনি AbortController ব্যবহার করতে পারেন ।

আরও দেখুন: https://www.npmjs.com/package/abortcontroller-polyfill

class FetchComponent extends React.Component{
  state = { todos: [] };
  
  controller = new AbortController();
  
  componentDidMount(){
    fetch('https://jsonplaceholder.typicode.com/todos',{
      signal: this.controller.signal
    })
    .then(res => res.json())
    .then(todos => this.setState({ todos }))
    .catch(e => alert(e.message));
  }
  
  componentWillUnmount(){
    this.controller.abort();
  }
  
  render(){
    return null;
  }
}

class App extends React.Component{
  state = { fetch: true };
  
  componentDidMount(){
    this.setState({ fetch: false });
  }
  
  render(){
    return this.state.fetch && <FetchComponent/>
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>


4
আমি যদি জানতাম যে অ্যাবোর্টকন্ট্রোলারের মতো অনুরোধগুলি বাতিল করার জন্য একটি ওয়েব এপিআই রয়েছে। তবে ঠিক আছে, এটি জানতে খুব বেশি দেরি হয়নি। ধন্যবাদ.
লেেক্স সফট

11

পোস্টটি খোলার পরে, একটি "অ্যাওরটেবল-ফেচ" যুক্ত করা হয়েছে। https://developers.google.com/web/updates/2017/09/abortable-fetch

(ডক্স থেকে :)

কন্ট্রোলার + সিগন্যাল কৌশলে অ্যাওর্টকন্ট্রোলার এবং অ্যাওর্টসিনগালের সাথে মিলিত হন:

const controller = new AbortController();
const signal = controller.signal;

নিয়ন্ত্রকের কেবল একটি পদ্ধতি রয়েছে:

controller.abort (); আপনি যখন এটি করেন, এটি সংকেতটি অবহিত করে:

signal.addEventListener('abort', () => {
  // Logs true:
  console.log(signal.aborted);
});

এই এপিআইটি ডিওএম স্ট্যান্ডার্ড দ্বারা সরবরাহ করা হয়েছে এবং এটিই সম্পূর্ণ এপিআই। এটি ইচ্ছাকৃতভাবে জেনেরিক তাই এটি অন্যান্য ওয়েব স্ট্যান্ডার্ড এবং জাভাস্ক্রিপ্ট লাইব্রেরি দ্বারা ব্যবহৃত হতে পারে।

উদাহরণস্বরূপ, আপনি কীভাবে 5 সেকেন্ড পরে আনতে পারেন:

const controller = new AbortController();
const signal = controller.signal;

setTimeout(() => controller.abort(), 5000);

fetch(url, { signal }).then(response => {
  return response.text();
}).then(text => {
  console.log(text);
});

আকর্ষণীয়, আমি এইভাবে চেষ্টা করব। তবে তার আগে, আমি প্রথমে অ্যাবোর্টকন্ট্রোলার এপিআই পড়ব।
লেক্স সফট

আমরা কি একাধিক ফেচের জন্য কেবলমাত্র একটি AbortController উদাহরণ ব্যবহার করতে পারি যে যখন আমরা এই একক AbortController এর অবতরণ পদ্ধতিটি উপাদানউইলউনমাউন্টে অনুরোধ করব তখন এটি আমাদের উপাদানগুলির মধ্যে থাকা সমস্ত ফেচকে বাতিল করে দেবে? যদি তা না হয়, তার অর্থ আমাদের প্রতিটি ফেচের জন্য বিভিন্ন অ্যাওর্টকন্ট্রোলার উদাহরণ সরবরাহ করতে হবে, তাই না?
লেেক্স সফট

3

এই সতর্কতার সংক্ষিপ্তসারটি হল যে আপনার উপাদানটির একটি উল্লেখ রয়েছে যা কিছু অসামান্য কলব্যাক / প্রতিশ্রুতি দ্বারা অনুষ্ঠিত হয়।

আপনার প্রতিস্থাপনের রাজ্যটিকে (যেমন আপনার উপাদানটি বাঁচিয়ে রাখে) দ্বিতীয় প্যাটার্নের মতো করে রাখার প্রতিষেধক এড়াতে প্রতিক্রিয়া ওয়েবসাইটটি একটি optionচ্ছিক প্রতিশ্রুতি ব্যবহার করার পরামর্শ দেয় ; তবে সেই কোডটি আপনার অবজেক্টকে বাঁচিয়ে রাখার জন্যও উপস্থিত হয়।

পরিবর্তে, আমি সেটস্টেটে নেস্টেড বাউন্ড ফাংশন সহ একটি ক্লোজার ব্যবহার করে এটি করেছি।

এখানে আমার নির্মাতা (টাইপ স্ক্রিপ্ট)…

constructor(props: any, context?: any) {
    super(props, context);

    let cancellable = {
        // it's important that this is one level down, so we can drop the
        // reference to the entire object by setting it to undefined.
        setState: this.setState.bind(this)
    };

    this.componentDidMount = async () => {
        let result = await fetch(…);            
        // ideally we'd like optional chaining
        // cancellable.setState?.({ url: result || '' });
        cancellable.setState && cancellable.setState({ url: result || '' });
    }

    this.componentWillUnmount = () => {
        cancellable.setState = undefined; // drop all references.
    }
}

4
এটি আদর্শভাবে মাউন্ট করা পতাকা রাখার চেয়ে আলাদা নয়, কেবল আপনি এটিকে ঝুলিয়ে রাখার পরিবর্তে বন্ধের সাথে আবদ্ধ করছেনthis
অনিলরেডশিফট

2

যখন আমাকে "সমস্ত সাবস্ক্রিপশন এবং অ্যাসিঙ্ক্রোনাস বাতিল করতে হবে" যখন আমি অন্য সমস্ত গ্রাহককে অবহিত করতে এবং প্রয়োজনে সার্ভারে বাতিল হওয়ার বিষয়ে আরও একটি অনুরোধ প্রেরণ করার জন্য আমি সাধারণত উপাদানটি উইলউনমઉન્ટটিতে রিডেক্সে কিছু প্রেরণ করি


2

আমি মনে করি যদি বাতিলকরণ সম্পর্কে সার্ভারকে অবহিত করার প্রয়োজন না হয় - সেরা পন্থাটি কেবলমাত্র async / প্রতীক্ষিত সিনট্যাক্স (এটি উপলব্ধ থাকলে) ব্যবহার করা।

constructor(props){
  super(props);
  this.state = {
    isLoading: true,
    dataSource: [{
      name: 'loading...',
      id: 'loading',
    }]
  }
}

async componentDidMount() {
  try {
    const responseJson = await fetch('LINK HERE')
      .then((response) => response.json());

    this.setState({
      isLoading: false,
      dataSource: responseJson,
    }
  } catch {
    console.error(error);
  }
}

0

গ্রহণযোগ্য সমাধানে বাতিলযোগ্য প্রতিশ্রুতি হুকের উদাহরণ ছাড়াও, useAsyncCallbackএকটি অনুরোধ কলব্যাক মোড়ানো এবং বাতিলযোগ্য প্রতিশ্রুতি ফিরিয়ে দেওয়া কোনও হুক থাকা সহজ । ধারণাটি একই, তবে একটি নিয়মিতর মতো হুক কাজ করছে useCallback। বাস্তবায়নের উদাহরণ এখানে:

function useAsyncCallback<T, U extends (...args: any[]) => Promise<T>>(callback: U, dependencies: any[]) {
  const isMounted = useRef(true)

  useEffect(() => {
    return () => {
      isMounted.current = false
    }
  }, [])

  const cb = useCallback(callback, dependencies)

  const cancellableCallback = useCallback(
    (...args: any[]) =>
      new Promise<T>((resolve, reject) => {
        cb(...args).then(
          value => (isMounted.current ? resolve(value) : reject({ isCanceled: true })),
          error => (isMounted.current ? reject(error) : reject({ isCanceled: true }))
        )
      }),
    [cb]
  )

  return cancellableCallback
}

-2

আমি মনে করি আমি এটির চারপাশে একটি উপায় পেয়েছি। সমস্যাটি নিজেই আনার মতো নয় তবে উপাদানটি বরখাস্ত হওয়ার পরে সেটস্টেট। সুতরাং সমাধানটি this.state.isMountedহিসাবে সেট করা ছিল falseএবং তারপরে componentWillMountএটি সত্যে পরিবর্তিত হবে এবং componentWillUnmountআবারও মিথ্যাতে সেট করা হবে। তারপরে if(this.state.isMounted)আনার ভিতরে কেবল সেটস্টেট। তাই ভালো:

  constructor(props){
    super(props);
    this.state = {
      isMounted: false,
      isLoading: true,
      dataSource: [{
        name: 'loading...',
        id: 'loading',
      }]
    }
  }

  componentDidMount(){
    this.setState({
      isMounted: true,
    })

    return fetch('LINK HERE')
      .then((response) => response.json())
      .then((responseJson) => {
        if(this.state.isMounted){
          this.setState({
            isLoading: false,
            dataSource: responseJson,
          }, function(){
          });
        }
      })
      .catch((error) =>{
        console.error(error);
      });
  }

  componentWillUnmount() {
    this.setState({
      isMounted: false,
    })
  }

4
সেটস্টেট সম্ভবত আদর্শ নয়, যেহেতু এটি তাত্ক্ষণিকভাবে রাষ্ট্রের মান আপডেট করে না।
লিওনএফ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.