प्रतिक्रिया - कैसे पता लगाया जाए कि उपयोगकर्ता को मूल घटक के सभी उप-घटक कब दिखाई देते हैं?


11

टी एल; डॉ

माता-पिता के घटक को कैसे पता चल सकता है जब इसके तहत प्रत्येक बच्चे के घटक का प्रतिपादन समाप्त हो गया है और उपयोगकर्ता को इसके सबसे संस्करण के साथ दिखाई दे रहा है।

मान लीजिए कि मेरे पास component Aएक बच्चा Gridघटक है जिसमें 3x3पोते के घटक शामिल हैं। उन पोते पोतों में से प्रत्येक डेटा को एक आरामदायक एपीआई एंडपॉइंट से प्राप्त करता है और डेटा उपलब्ध होने पर खुद को प्रस्तुत करता है।

मैं Component Aएक loaderप्लेसहोल्डर के साथ पूरे क्षेत्र को कवर करना चाहता हूं , केवल तब अनावरण किया जाना चाहिए जब ग्रिड में घटकों में से आखिरी ने डेटा को सफलतापूर्वक प्राप्त किया हो, और इसे प्रदान किया हो, जैसे कि यह पहले से ही डोम पर है और इसे देखा जा सकता है।

उपयोगकर्ता का अनुभव "लोडर" से बिना चंचलता के पूरी तरह से आबादी वाले ग्रिड में एक सुपर चिकनी संक्रमण होना चाहिए।

मेरी समस्या ठीक से जान रही है कि लोडर के तहत घटकों का अनावरण कब करना है।

क्या कोई ऐसा तंत्र है जिस पर मैं पूरी सटीकता के साथ भरोसा कर सकता हूं? मैं लोडर के लिए समय सीमा को हार्ड कोड नहीं करता। जैसा कि मैं समझता हूं कि ComponentDidMountप्रत्येक बच्चे के लिए भरोसा करना भी अविश्वसनीय है क्योंकि यह वास्तव में गारंटी नहीं देता है कि कॉल के समय घटक पूरी तरह से उपयोगकर्ता को दिखाई देता है।

प्रश्न को और भी दूर करने के लिए:

मेरे पास एक घटक है जो किसी प्रकार के डेटा को प्रस्तुत करता है। इसके आरंभिक होने के बाद इसमें यह नहीं है, इसलिए इसके लिए componentDidMountयह एक एपीआई एंडपॉइंट को हिट करता है। एक बार जब यह डेटा प्राप्त करता है, तो इसे प्रतिबिंबित करने के लिए अपनी स्थिति बदल रहा है। यह उस घटक की अंतिम स्थिति को फिर से प्रस्तुत करता है। मेरा प्रश्न यह है: मुझे कैसे पता चलेगा कि जब पुन: रेंडर हुआ है और उपयोगकर्ता का सामना करना पड़ डोम में परिलक्षित होता है। उस समय में! = उस समय में बिंदु जब घटक की स्थिति डेटा को बदलने के लिए बदल गई है।


राज्य का प्रबंधन कैसे कर रहे हैं? Redux का उपयोग करना पसंद है? या यह पूरी तरह से घटकों के साथ है?
TechTurtle

यह घटकों के भीतर है, लेकिन इसे बाह्यीकृत किया जा सकता है। मैं अन्य चीजों के लिए Redux का उपयोग करता हूं।
जेसनगेन

और आपको कैसे पता चलेगा कि ग्रिड के अंतिम घटक ने डेटा प्राप्त करना समाप्त कर दिया है?
TechTurtle

मैं नही। ग्रिड घटकों को एक दूसरे के बारे में पता नहीं है। प्रत्येक उप-घटकों में ComponentDidMountमैं axiosडेटा लाने के लिए उपयोग कर रहा हूं । जब डेटा के माध्यम से आता है, मैं उस घटक की स्थिति को बदल देता हूं जो डेटा के एक रेंडर का कारण बनता है। सिद्धांत रूप में, 8 बच्चे घटक 3 सेकंड के भीतर
ला

1
मैं ऐसा कर सकता हूं, लेकिन मुझे कैसे पता चलेगा कि लोडर ओवरले का अनावरण कब करना है ताकि उपयोगकर्ता कभी नहीं देखेंगे कि एक घटक "खाली" से "पूर्ण" कैसे जाता है? यह डेटा लाने के बारे में सवाल नहीं है। यह प्रतिपादन के बारे में है ... भले ही मेरे पास केवल एक बच्चा घटक था। ComponentDidMount पर्याप्त नहीं है। मुझे यह जानने की जरूरत है कि पोस्ट-डेटा-फ़्रीचिंग-रेंडर कब पूरा हुआ है और डोम पूरी तरह से अपडेट है ताकि मैं लोडर ओवरले का अनावरण कर सकूं।
जेसनगेन

जवाबों:


5

रिएक्ट में दो जीवन चक्र हुक होते हैं जिन्हें घटक के DOM द्वारा प्रस्तुत किए जाने के बाद कहा जाता है:

आपके उपयोग के मामले में आपके पैरेंट कंपोनेंट P की दिलचस्पी तब होती है जब N चाइल्ड कंपोनेंट्स में से प्रत्येक ने कुछ कंडीशन एक्स को संतुष्ट किया हो । X को अनुक्रम के रूप में परिभाषित किया जा सकता है:

  • async ऑपरेशन पूरा हुआ
  • घटक प्रदान किया गया है

घटक की स्थिति के संयोजन और componentDidUpdateहुक का उपयोग करके , आप जान सकते हैं कि अनुक्रम कब पूरा हुआ है और आपका घटक स्थिति X से मिलता है।

जब आप अपने async ऑपरेशन को स्टेट वेरिएबल सेट करके पूरा कर लेते हैं, तो आप उसे ट्रैक कर सकते हैं। उदाहरण के लिए:

this.setState({isFetched: true})

राज्य की स्थापना के बाद, प्रतिक्रिया आपके घटकों को कॉल करेगी componentDidUpdate। इस फ़ंक्शन के भीतर वर्तमान और पिछले राज्य की वस्तुओं की तुलना करके आप मूल घटक को संकेत कर सकते हैं कि आपका async ऑपरेशन पूरा हो चुका है और आपके नए घटक की स्थिति का प्रतिपादन किया गया है:

componentDidUpdate(_prevProps, prevState) {
  if (this.state.isFetched === true && this.state.isFetched !== prevState.isFetched) {
    this.props.componentHasMeaningfullyUpdated()
  }
}

अपने P घटक में, आप इस बात का ध्यान रखने के लिए एक काउंटर का उपयोग कर सकते हैं कि कितने बच्चों ने सार्थक रूप से अपडेट किया है:

function onComponentHasMeaningfullyUpdated() {
  this.setState({counter: this.state.counter + 1})
}

अंत में, N की लंबाई को जानकर आप जान सकते हैं कि सभी सार्थक अपडेट कब हुए हैं और P के आपके रेंडर मेथड के अनुसार कार्य करते हैं :

const childRenderingFinished = this.state.counter >= N

1

मैं इसे स्थापित करूंगा ताकि आप अपने घटकों को यह बताने के लिए एक वैश्विक स्थिति चर भरोसा कर रहे हों कि कब प्रस्तुत करना है। Redux इस परिदृश्य के लिए बेहतर है जहां कई घटक एक दूसरे से बात कर रहे हैं, और आपने एक टिप्पणी में उल्लेख किया है कि आप कभी-कभी इसका उपयोग करते हैं। इसलिए मैं Redux का उपयोग कर एक उत्तर दूंगा।

तुम्हें पता है, पैरेंट कंटेनर के लिए अपने API कॉल स्थानांतरित करने के लिए होगा Component A। यदि आप चाहते हैं कि एपीआई कॉल पूरी होने के बाद ही आप अपने पोते को प्रस्तुत करें, तो आप उन एपीआई कॉलों को स्वयं पोते में नहीं रख सकते। एक एपीआई कॉल एक घटक से कैसे किया जा सकता है जो अभी तक मौजूद नहीं है?

एक बार सभी एपीआई कॉल किए जाने के बाद, आप डेटा ऑब्जेक्ट्स के एक समूह वाले वैश्विक राज्य चर को अपडेट करने के लिए क्रियाओं का उपयोग कर सकते हैं। हर बार डेटा प्राप्त होने पर (या कोई त्रुटि पकड़ी जाने पर), आप यह जांचने के लिए एक कार्रवाई भेज सकते हैं कि क्या आपका डेटा ऑब्जेक्ट पूरी तरह से भरा हुआ है। एक बार इसका पूरी तरह से भर जाने के बाद, आप एक loadingचर को अपडेट कर सकते हैं false, और सशर्त रूप से अपने Gridघटक को प्रस्तुत कर सकते हैं।

उदाहरण के लिए:

// Component A

import { acceptData, catchError } from '../actions'

class ComponentA extends React.Component{

  componentDidMount () {

    fetch('yoururl.com/data')
      .then( response => response.json() )
      // send your data to the global state data array
      .then( data => this.props.acceptData(data, grandChildNumber) )
      .catch( error => this.props.catchError(error, grandChildNumber) )

    // make all your fetch calls here

  }

  // Conditionally render your Loading or Grid based on the global state variable 'loading'
  render() {
    return (
      { this.props.loading && <Loading /> }
      { !this.props.loading && <Grid /> }
    )
  }

}


const mapStateToProps = state => ({ loading: state.loading })

const mapDispatchToProps = dispatch => ({ 
  acceptData: data => dispatch( acceptData( data, number ) )
  catchError: error=> dispatch( catchError( error, number) )
})
// Grid - not much going on here...

render () {
  return (
    <div className="Grid">
      <GrandChild1 number={1} />
      <GrandChild2 number={2} />
      <GrandChild3 number={3} />
      ...
      // Or render the granchildren from an array with a .map, or something similar
    </div>
  )
}
// Grandchild

// Conditionally render either an error or your data, depending on what came back from fetch
render () {
  return (
    { !this.props.data[this.props.number].error && <Your Content Here /> }
    { this.props.data[this.props.number].error && <Your Error Here /> }
  )
}

const mapStateToProps = state => ({ data: state.data })

आपका reducer वैश्विक राज्य ऑब्जेक्ट को रोक देगा जो कहेगा कि चीजें अभी तक जाने के लिए तैयार हैं या नहीं:

// reducers.js

const initialState = {
  data: [{},{},{},{}...], // 9 empty objects
  loading: true
}

const reducers = (state = initialState, action) {
  switch(action.type){

    case RECIEVE_SOME_DATA:
      return {
        ...state,
        data: action.data
      }

     case RECIEVE_ERROR:
       return {
         ...state,
         data: action.data
       }

     case STOP_LOADING:
       return {
         ...state,
         loading: false
       }

  }
}

आपके कार्यों में:


export const acceptData = (data, number) => {
  // First revise your data array to have the new data in the right place
  const updatedData = data
  updatedData[number] = data
  // Now check to see if all your data objects are populated
  // and update your loading state:
  dispatch( checkAllData() )
  return {
    type: RECIEVE_SOME_DATA,
    data: updatedData,
  }
}

// error checking - because you want your stuff to render even if one of your api calls 
// catches an error
export const catchError(error, number) {
  // First revise your data array to have the error in the right place
  const updatedData = data
  updatedData[number].error = error
  // Now check to see if all your data objects are populated
  // and update your loading state:
  dispatch( checkAllData() )
  return {
    type: RECIEVE_ERROR,
    data: updatedData,
  }
}

export const checkAllData() {
  // Check that every data object has something in it
  if ( // fancy footwork to check each object in the data array and see if its empty or not
    store.getState().data.every( dataSet => 
      Object.entries(dataSet).length === 0 && dataSet.constructor === Object ) ) {
        return {
          type: STOP_LOADING
        }
      }
  }

अलग

यदि आप वास्तव में इस विचार से विवाहित हैं कि आपके एपीआई कॉल प्रत्येक पोते के अंदर रहते हैं, लेकिन यह कि पोते की पूरी ग्रिड तब तक रेंडर नहीं करती है जब तक कि सभी एपीआई कॉल पूरी नहीं हो जाती हैं, तो आपको पूरी तरह से अलग समाधान का उपयोग करना होगा। इस स्थिति में, आपके पोते को अपनी कॉल करने के लिए शुरू से प्रस्तुत करना होगा, लेकिन इसके साथ एक सीएसएस क्लास है display: none, जो वैश्विक राज्य चर के बाद केवल loadingगलत के रूप में चिह्नित होने के बाद बदलता है। यह भी उल्लेखनीय है, लेकिन प्रतिक्रिया के बिंदु के अलावा क्रमबद्ध है।


1

आप संभावित रूप से रिएक्ट सस्पेंस का उपयोग करके इसे हल कर सकते हैं ।

चेतावनी यह है कि रेंडर करने वाले कंपोनेंट ट्री को सस्पेंड करना कोई अच्छा आइडिया नहीं है (यानी: यदि आपका कंपोनेंट रेंडर प्रक्रिया को बंद कर देता है, तो मेरे अनुभव में उस कंपोनेंट को सस्पेंड करना अच्छा नहीं है), इसलिए यह है शायद एक बेहतर विचार है कि घटक जो कोशिकाओं को प्रस्तुत करता है , उसमें अनुरोधों को किक करें । कुछ इस तरह:

export default function App() {
  const cells = React.useMemo(
    () =>
      ingredients.map((_, index) => {
        // This starts the fetch but *does not wait for it to finish*.
        return <Cell resource={fetchIngredient(index)} />;
      }),
    []
  );

  return (
    <div className="App">
      <Grid>{cells}</Grid>
    </div>
  );
}

अब, Redux के साथ काफी सस्पेंस जोड़े मुझे यकीन नहीं है। सस्पेंस के इस (प्रायोगिक!) संस्करण के पीछे का पूरा विचार यह है कि आप एक मूल घटक के रेंडर चक्र के दौरान तुरंत भ्रूण को शुरू करते हैं और एक ऐसी वस्तु को पास करते हैं जो बच्चों के लिए एक भ्रूण का प्रतिनिधित्व करती है। यह आपको किसी प्रकार की बैरियर ऑब्जेक्ट (जो आपको अन्य दृष्टिकोणों में आवश्यकता होगी) होने से रोकता है।

मैं कहूंगा कि मुझे तब तक इंतजार नहीं करना चाहिए जब तक कि कुछ भी प्रदर्शित करने के लिए सबकुछ सही नहीं होता है क्योंकि तब यूआई सबसे धीमा कनेक्शन जितना धीमा होगा या हो सकता है कि बिल्कुल भी काम न करे!

यहाँ लापता कोड बाकी है:

const ingredients = [
  "Potato",
  "Cabbage",
  "Beef",
  "Bok Choi",
  "Prawns",
  "Red Onion",
  "Apple",
  "Raisin",
  "Spinach"
];

function randomTimeout(ms) {
  return Math.ceil(Math.random(1) * ms);
}

function fetchIngredient(id) {
  const task = new Promise(resolve => {
    setTimeout(() => resolve(ingredients[id]), randomTimeout(5000));
  });

  return new Resource(task);
}

// This is a stripped down version of the Resource class displayed in the React Suspense docs. It doesn't handle errors (and probably should).
// Calling read() will throw a Promise and, after the first event loop tick at the earliest, will return the value. This is a synchronous-ish API,
// Making it easy to use in React's render loop (which will not let you return anything other than a React element).
class Resource {
  constructor(promise) {
    this.task = promise.then(value => {
      this.value = value;
      this.status = "success";
    });
  }

  read() {
    switch (this.status) {
      case "success":
        return this.value;

      default:
        throw this.task;
    }
  }
}

function Cell({ resource }) {
  const data = resource.read();
  return <td>{data}</td>;
}

function Grid({ children }) {
  return (
    // This suspense boundary will cause a Loading sign to be displayed if any of the children suspend (throw a Promise).
    // Because we only have the one suspense boundary covering all children (and thus Cells), the fallback will be rendered
    // as long as at least one request is in progress.
    // Thanks to this approach, the Grid component need not be aware of how many Cells there are.
    <React.Suspense fallback={<h1>Loading..</h1>}>
      <table>{children}</table>
    </React.Suspense>
  );
}

और एक सैंडबॉक्स: https://codesandbox.io/s/falling-dust-b8e7s


1
बस टिप्पणियों को पढ़ें .. मुझे विश्वास नहीं है कि डोम में प्रस्तुत किए जाने वाले सभी घटकों के लिए प्रतीक्षा करना तुच्छ होगा - और यह एक अच्छे कारण के लिए है। यह वास्तव में बहुत हैकरी लगता है। आप क्या हासिल करने का प्रयास कर रहे हैं?
दान पेंट्री

1

सबसे पहले, जीवन-चक्र में async / प्रतीक्षा विधि हो सकती है।

क्या हम के बारे में पता करने की जरूरत componentDidMountहै और componentWillMount,

ComponentsWillMount को पहले माता-पिता फिर बच्चा कहा जाता है।
घटकडोमाउंट इसके विपरीत करते हैं।

मेरे विचार में, सामान्य जीवन-चक्र का उपयोग करके उन्हें लागू करना काफी अच्छा होगा।

  • बाल घटक
async componentDidMount() {
  await this.props.yourRequest();
  // Check if satisfied, if true, add flag to redux store or something,
  // or simply check the res stored in redux in parent leading no more method needed here
  await this.checkIfResGood();
}
  • मूल घटक
// Initial state `loading: true`
componentDidUpdate() {
  this.checkIfResHaveBad(); // If all good, `loading: false`
}
...
{this.state.loading ? <CircularProgress /> : <YourComponent/>}

सामग्री- UI सर्कुलरप्रोग्रेस

चूंकि बच्चे ने माता-पिता को ऐसा करने के लिए पुन: प्रतिपादन किया है, इसलिए इसे पकड़ना didUpdateठीक रहेगा।
और, जब से यह प्रतिपादन के बाद कहा जाता है, पृष्ठों को उसके बाद नहीं बदला जाना चाहिए यदि आप अपनी मांग के अनुसार चेक फ़ंक्शन सेट करते हैं।

हम इस कार्यान्वयन का उपयोग अपने ठेस में करते हैं, जिसमें एक एपीआई होता है जिससे प्रतिक्रिया प्राप्त करने के लिए 30 एस होता है, जहां तक ​​मुझे लगता है कि सभी ने अच्छी तरह से काम किया।

यहाँ छवि विवरण दर्ज करें


0

जैसा कि आप में एक API कॉल कर रहे हैं componentDidMount, जब API कॉल का समाधान हो जाएगा आप में डेटा होगा componentDidUpdateके रूप में इस जीवन चक्र आप पारित करेंगे prevPropsऔर prevState। तो आप नीचे कुछ ऐसा कर सकते हैं:

class Parent extends Component {
  getChildUpdateStatus = (data) => {
 // data is any data you want to send from child
}
render () {
 <Child sendChildUpdateStatus={getChildUpdateStatus}/>
}
}

class Child extends Component {
componentDidUpdate = (prevProps, prevState) => {
   //compare prevProps from this.props or prevState from current state as per your requirement
  this.props.sendChildUpdateStatus();
}

render () { 
   return <h2>{.. child rendering}</h2>
  }
}

यदि आप घटक पुनः प्रस्तुत करने से पहले यह जानना चाहते हैं, तो आप उपयोग कर सकते हैं getSnapshotBeforeUpdate

https://reactjs.org/docs/react-component.html#getsnapshotbeforeupdate


0

आप के साथ जा सकते हैं कई दृष्टिकोण हैं:

  1. सबसे आसान तरीका बाल घटक को कॉलबैक पास करना है। ये बाल घटक तब इस कॉलबैक को कॉल कर सकते हैं जैसे ही वे अपने व्यक्तिगत डेटा या किसी अन्य व्यावसायिक तर्क को लाने के बाद प्रदान करते हैं जिसे आप रखना चाहते हैं। यहाँ उसी के लिए एक सैंडबॉक्स दिया गया है: https://codesandbox.io/s/unruffled-shockley-yay3x3
  2. निर्भर / बच्चे के घटकों को अपडेट करने के लिए कहें, जब उन्हें एक वैश्विक ऐप राज्य में भ्रूण डेटा प्रदान किया जाता है, जिसे आपका घटक सुन रहा होगा
  3. आप इस पैरेंट घटक के लिए स्थानीयकृत स्थिति बनाने के लिए React.useContext का भी उपयोग कर सकते हैं। बाल घटक तब इस संदर्भ को अपडेट कर सकते हैं जब उन्हें भ्रूण के डेटा को रेंडर करने के साथ किया जाता है। फिर, मूल घटक इस संदर्भ में सुन रहा होगा और तदनुसार कार्य कर सकता है
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.