प्रतिक्रिया में बच्चे की स्थिति का उपयोग कैसे करें?


214

मेरे पास निम्नलिखित संरचना है:

FormEditor- FieldEditorएक से अधिक क्षेत्ररक्षक रखता है - फॉर्म का एक क्षेत्र संपादित करता है और इसके बारे में विभिन्न मूल्यों को सहेजता है

जब FormEditor के भीतर एक बटन पर क्लिक किया जाता है, तो मैं सभी FieldEditorघटकों से उन क्षेत्रों के बारे में जानकारी एकत्र करने में सक्षम होना चाहता हूं , जो जानकारी उनके राज्य में है, और यह सभी FormEditor के भीतर है।

मैंने FieldEditorराज्य के बाहर के खेतों के बारे में जानकारी संग्रहीत करने पर विचार किया और FormEditorइसके बजाय इसे राज्य में रखा । हालाँकि, FormEditorइसके प्रत्येक FieldEditorघटक को सुनने की आवश्यकता होगी क्योंकि वे अपनी स्थिति को बदलते और संग्रहीत करते हैं।

क्या मैं इसके बजाय सिर्फ बच्चों की स्थिति तक नहीं पहुँच सकता? क्या यह आदर्श है?


4
"क्या मैं इसके बजाय बच्चों की स्थिति तक पहुँच नहीं सकता? क्या यह आदर्श है?" नहीं। राज्य कुछ आंतरिक है और बाहर से रिसाव नहीं होना चाहिए। आप अपने घटक के लिए एक्सेसर विधि लागू कर सकते हैं, लेकिन यह भी आदर्श नहीं है।
फेलिक्स क्लिंग

@FelixKling तब आप सुझाव दे रहे हैं कि बच्चे के लिए माता-पिता के संचार का आदर्श तरीका केवल घटनाएं हैं?
मोशे रेवाह

3
हां, घटनाएं एक तरह से हैं। या फ्लक्स की तरह एक दिशात्मक डेटा प्रवाह को बढ़ावा देता है: facebook.github.io/flux
फेलिक्स क्लिंग

1
यदि आप FieldEditorअलग से उपयोग नहीं करने जा रहे हैं , तो उनकी स्थिति को FormEditorअच्छा लगता है। यदि यह स्थिति है, तो आपके FieldEditorउदाहरण propsउनके प्रपत्र संपादक द्वारा पारित किए गए आधार पर प्रस्तुत करेंगे , न कि उनके state। एक अधिक जटिल लेकिन लचीला तरीका यह होगा कि एक धारावाहिक बनाया जाए जो किसी भी कंटेनर के बच्चों के माध्यम से जाता है और FormEditorउनके बीच सभी उदाहरणों को खोजता है और उन्हें JSON ऑब्जेक्ट में अनुक्रमित करता है। फॉर्म संपादक में इंस्टेंस के नेस्टिंग स्तरों के आधार पर JSON ऑब्जेक्ट को वैकल्पिक रूप से नेस्टेड (एक से अधिक स्तर) किया जा सकता है।
13

4
मुझे लगता है कि रिएक्ट डॉक्स 'लिफ्टिंग स्टेट अप' ऐसा करने का शायद सबसे 'रिएक्टी' तरीका है
icc97

जवाबों:


135

यदि आपके पास पहले से ही अलग-अलग FieldEditors के लिए onChange हैंडलर है, तो मैं यह नहीं देखता कि आप केवल FormEditor घटक तक स्थिति को स्थानांतरित क्यों नहीं कर सकते हैं और बस वहां से एक कॉलबैक पास कर सकते हैं जो कि FieldEditors को अद्यतन करेगा जो मूल स्थिति को अद्यतन करेगा। ऐसा लगता है कि यह करने के लिए एक और प्रतिक्रिया-वाई तरीका है, मुझे करने के लिए।

इस की रेखा के साथ कुछ शायद:

const FieldEditor = ({ value, onChange, id }) => {
  const handleChange = event => {
    const text = event.target.value;
    onChange(id, text);
  };

  return (
    <div className="field-editor">
      <input onChange={handleChange} value={value} />
    </div>
  );
};

const FormEditor = props => {
  const [values, setValues] = useState({});
  const handleFieldChange = (fieldId, value) => {
    setValues({ ...values, [fieldId]: value });
  };

  const fields = props.fields.map(field => (
    <FieldEditor
      key={field}
      id={field}
      onChange={handleFieldChange}
      value={values[field]}
    />
  ));

  return (
    <div>
      {fields}
      <pre>{JSON.stringify(values, null, 2)}</pre>
    </div>
  );
};

// To add abillity to dynamically add/remove fields keep the list in state
const App = () => {
  const fields = ["field1", "field2", "anotherField"];

  return <FormEditor fields={fields} />;
};

मूल - पूर्व-हुक संस्करण:


2
यह onChange के लिए उपयोगी है, लेकिन onSubmit में उतना नहीं।
190290000 रूबल मैन

1
@ १ ९ ०२२ ९ ००,००० नर्वसमैन मुझे यकीन नहीं है कि आपका क्या मतलब है, लेकिन चूंकि अलग-अलग क्षेत्रों पर ऑनकालेर्स यह सुनिश्चित करते हैं कि आपके फॉर्मएडिटर घटक में आपके राज्य में आपके पास हमेशा पूर्ण प्रपत्र डेटा उपलब्ध है, इसलिए आपका ऑनस्मिट केवल उसी तरह शामिल होगा myAPI.SaveStuff(this.state);। यदि आप थोड़ा और विस्तृत करते हैं, तो शायद मैं आपको एक बेहतर उत्तर दे सकता हूं :)
मार्कस-आईपीस

मान लें कि मेरे पास अलग-अलग प्रकार के 3 फ़ील्ड हैं, जिनमें से प्रत्येक की अपनी मान्यता है। मैं सबमिट करने से पहले उनमें से प्रत्येक को मान्य करना चाहता हूं। यदि सत्यापन विफल रहता है, तो त्रुटि वाले प्रत्येक फ़ील्ड को रेंडर करना चाहिए। मुझे नहीं पता कि आपके प्रस्तावित समाधान के साथ यह कैसे करना है, लेकिन शायद एक तरीका है!
190290000 रूबल मैन

1
@ 190290000RubleMan मूल प्रश्न की तुलना में एक अलग मामले की तरह लगता है। अधिक विवरण और शायद कुछ नमूना कोड के साथ एक नया प्रश्न खोलें, और मैं बाद में देख सकता हूं :)
मार्कस-आईपीस

आपको यह उत्तर उपयोगी लग सकता है : यह राज्य हुक का उपयोग करता है, यह कवर करता है कि राज्य कहाँ बनाया जाना चाहिए, कैसे प्रत्येक कीस्ट्रोक पर पुन: रेंडर करने से बचें, फ़ील्ड सत्यापन कैसे करें, आदि
एंड्रयू

189

इससे पहले कि मैं इस बारे में विस्तार से बताऊं कि आप बच्चे के घटक की स्थिति तक कैसे पहुंच सकते हैं, कृपया इस विशेष परिदृश्य को संभालने के लिए बेहतर समाधान के बारे में मार्कस-आईपीस के उत्तर को पढ़ना सुनिश्चित करें ।

यदि आप वास्तव में घटक के बच्चों की स्थिति तक पहुंचने की इच्छा रखते हैं, तो आप refप्रत्येक बच्चे को एक संपत्ति सौंप सकते हैं । संदर्भों को लागू करने के अब दो तरीके हैं: React.createRef()रीफ़ का उपयोग करना और कॉलबैक करना ।

का उपयोग करते हुए React.createRef()

यह वर्तमान में प्रतिक्रिया 16.3 के रूप में संदर्भ का उपयोग करने के लिए अनुशंसित तरीका है ( अधिक जानकारी के लिए डॉक्स देखें )। यदि आप पहले वाले संस्करण का उपयोग कर रहे हैं तो कॉलबैक संदर्भों के बारे में नीचे देखें।

आपको अपने मूल घटक के निर्माता में एक नया संदर्भ बनाने की आवश्यकता होगी और फिर उसे एक बच्चे को refविशेषता के माध्यम से असाइन करना होगा ।

class FormEditor extends React.Component {
  constructor(props) {
    super(props);
    this.FieldEditor1 = React.createRef();
  }
  render() {
    return <FieldEditor ref={this.FieldEditor1} />;
  }
}

इस तरह के रेफरी तक पहुंचने के लिए, आपको उपयोग करने की आवश्यकता होगी:

const currentFieldEditor1 = this.FieldEditor1.current;

यह माउंट किए गए घटक का एक उदाहरण लौटाएगा ताकि आप फिर currentFieldEditor1.stateराज्य तक पहुंचने के लिए उपयोग कर सकें ।

बस यह कहने के लिए कि आप एक घटक के बजाय एक डोम नोड पर इन संदर्भों का उपयोग करते हैं (जैसे <div ref={this.divRef} />) तो this.divRef.currentएक घटक उदाहरण के बजाय अंतर्निहित DOM तत्व वापस आ जाएगा।

कॉलबैक Refs

यह गुण कॉलबैक फ़ंक्शन लेता है जो संलग्न घटक के संदर्भ में पारित किया जाता है। घटक को माउंट या अनमाउंट किए जाने के तुरंत बाद इस कॉलबैक को निष्पादित किया जाता है।

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

<FieldEditor
    ref={(fieldEditor1) => {this.fieldEditor1 = fieldEditor1;}
    {...props}
/>

इन उदाहरणों में संदर्भ को मूल घटक पर संग्रहीत किया जाता है। इस घटक को अपने कोड में कॉल करने के लिए, आप इसका उपयोग कर सकते हैं:

this.fieldEditor1

और फिर this.fieldEditor1.stateराज्य प्राप्त करने के लिए उपयोग करें।

एक बात पर ध्यान दें, सुनिश्चित करें कि आपके बच्चे के घटक ने इसे एक्सेस करने का प्रयास करने से पहले रेंडर किया है ^ _ ^

ऊपर के रूप में, यदि आप एक घटक (जैसे <div ref={(divRef) => {this.myDiv = divRef;}} />) के बजाय एक डोम नोड पर इन संदर्भों का उपयोग करते हैं तो this.divRefघटक उदाहरण के बजाय अंतर्निहित DOM तत्व वापस कर देंगे।

अग्रिम जानकारी

यदि आप रिएक्ट की रेफ प्रॉपर्टी के बारे में अधिक पढ़ना चाहते हैं, तो फेसबुक से इस पेज को देखें।

सुनिश्चित करें कि आपने " डोंट ओवरयूज रेफ्स " खंड को पढ़ा है जो कहता है कि आपको state"चीजों को बनाने" के लिए बच्चे का उपयोग नहीं करना चाहिए ।

आशा है कि यह मदद करता है ^ _ ^

संपादित करें: React.createRef()रेफरी बनाने के लिए जोड़ा गया तरीका। ईएस 5 कोड हटा दिया गया।


5
इसके अलावा साफ छोटे tidbit, आप से कार्य कॉल कर सकते हैं this.refs.yourComponentNameHere। मैंने इसे फ़ंक्शन के माध्यम से स्थिति बदलने के लिए उपयोगी पाया है। उदाहरण: this.refs.textInputField.clearInput();
डायलन पियर्स

7
खबरदार (डॉक्स से): Refs एक विशेष तरीके से एक बच्चे को एक संदेश भेजने का एक शानदार तरीका है, जो स्ट्रीमिंग रिएक्टिव propsऔर स्ट्रीमिंग के माध्यम से करने के लिए असुविधाजनक होगा state। हालांकि, आपको अपने एप्लिकेशन के माध्यम से डेटा प्रवाहित करने के लिए अपने गो-एब्सट्रैक्ट नहीं होना चाहिए। डिफ़ॉल्ट रूप से, प्रतिक्रियाशील डेटा प्रवाह का उपयोग करें और refउन मामलों के उपयोग के लिए सहेजें जो स्वाभाविक रूप से गैर-प्रतिक्रियाशील हैं।
लिन

2
मुझे केवल वह राज्य मिलता है जिसे कंस्ट्रक्टर के अंदर परिभाषित किया गया था (राज्य की तारीख तक नहीं)
व्लादो पांडेसिक

3
Refs

क्या यह करना संभव है यदि बच्चा घटक एक रेडक्स connectedघटक है?
जमनचेरी

7

इसका 2020 और आप में से बहुत से लोग एक समान समाधान की तलाश में यहां आएंगे लेकिन हुक (वे महान हैं!) और कोड स्वच्छता और वाक्यविन्यास के संदर्भ में नवीनतम दृष्टिकोणों के साथ।

इसलिए जैसा कि पिछले जवाबों में कहा गया था, इस तरह की समस्या के लिए सबसे अच्छा तरीका यह है कि बच्चे के घटक के बाहर की स्थिति को पकड़ कर रखा जाए fieldEditor। आप ऐसा कई तरीकों से कर सकते हैं।

सबसे "जटिल" वैश्विक संदर्भ (राज्य) के साथ है, जो माता-पिता और बच्चे दोनों तक पहुंच और संशोधित कर सकते हैं। इसका एक महान समाधान है जब घटक पेड़ की पदानुक्रम में बहुत गहरे हैं और इसलिए प्रत्येक स्तर में प्रॉप्स भेजने के लिए महंगा है।

इस मामले में मुझे लगता है कि इसके लायक नहीं है, और अधिक सरल दृष्टिकोण हमें उन परिणामों को लाएगा जो हम चाहते हैं, बस शक्तिशाली का उपयोग करके React.useState()

React.useState () हुक के साथ दृष्टिकोण, कक्षा के घटकों की तुलना में सरल

जैसा कि हमने कहा कि हम fieldEditorअपने माता-पिता में अपने बच्चे के घटक के डेटा में परिवर्तन से निपटेंगे और संग्रहीत करेंगे fieldForm। ऐसा करने के लिए, हम उस फ़ंक्शन का संदर्भ भेजेंगे जो fieldFormराज्य में परिवर्तनों से निपटेगा और लागू करेगा , आप ऐसा कर सकते हैं:

function FieldForm({ fields }) {
  const [fieldsValues, setFieldsValues] = React.useState({});
  const handleChange = (event, fieldId) => {
    let newFields = { ...fieldsValues };
    newFields[fieldId] = event.target.value;

    setFieldsValues(newFields);
  };

  return (
    <div>
      {fields.map(field => (
        <FieldEditor
          key={field}
          id={field}
          handleChange={handleChange}
          value={fieldsValues[field]}
        />
      ))}
      <div>{JSON.stringify(fieldsValues)}</div>
    </div>
  );
}

ध्यान दें कि React.useState({})स्थिति 0 के साथ एक सरणी वापस आ जाएगी कॉल पर निर्दिष्ट मूल्य (इस मामले में खाली वस्तु), और स्थिति 1 मान को संशोधित करने वाले फ़ंक्शन का संदर्भ है।

अब चाइल्ड कंपोनेंट के साथ FieldEditor, आपको रिटर्न स्टेटमेंट के साथ फंक्शन बनाने की भी जरूरत नहीं है, एरो फंक्शन वाला लीन कंटीन्यू करेगा!

const FieldEditor = ({ id, value, handleChange }) => (
  <div className="field-editor">
    <input onChange={event => handleChange(event, id)} value={value} />
  </div>
);

Aaaaand हम कर रहे हैं, और कुछ नहीं, बस इन दो कीचड़ कार्यात्मक घटकों के साथ हम अपने अंत लक्ष्य "हमारे बच्चे के FieldEditorमूल्य " का उपयोग करते हैं और इसे अपने माता-पिता में दिखाते हैं।

आप 5 साल पहले से स्वीकृत उत्तर की जांच कर सकते हैं और देखें कि हुक ने रिएक्ट कोड कैसे दुबला बनाया (बहुत कुछ!)।

आशा है कि मेरा उत्तर आपको हुक के बारे में अधिक जानने और समझने में मदद करता है, और यदि आप यहां एक काम करने वाले उदाहरण की जांच करना चाहते हैं तो यह है


4

अब आप InputField की स्थिति तक पहुँच सकते हैं जो कि FormEditor का बच्चा है।

मूल रूप से जब भी इनपुट क्षेत्र (बच्चे) की स्थिति में कोई परिवर्तन होता है तो हमें ईवेंट ऑब्जेक्ट से मान मिल रहा है और फिर इस मान को उस पेरेंट को पास करना है जहां पेरेंट में राज्य सेट है।

बटन पर क्लिक करने पर हम केवल इनपुट फ़ील्ड की स्थिति को प्रिंट कर रहे हैं।

यहाँ पर मुख्य बात यह है कि हम इनपुट फील्ड की आईडी / वैल्यू प्राप्त करने के लिए प्रॉप्स का उपयोग कर रहे हैं और उन कार्यों को कॉल करने के लिए भी हैं जो इनपुट फ़ील्ड पर विशेषताओं के रूप में सेट किए जाते हैं, जबकि हम पुन: प्रयोज्य चाइल्ड इनपुट फ़ील्ड बनाते हैं।

class InputField extends React.Component{
  handleChange = (event)=> {
    const val = event.target.value;
    this.props.onChange(this.props.id , val);
  }

  render() {
    return(
      <div>
        <input type="text" onChange={this.handleChange} value={this.props.value}/>
        <br/><br/>
      </div>
    );
  }
}       


class FormEditorParent extends React.Component {
  state = {};
  handleFieldChange = (inputFieldId , inputFieldValue) => {
    this.setState({[inputFieldId]:inputFieldValue});
  }
  //on Button click simply get the state of the input field
  handleClick = ()=>{
    console.log(JSON.stringify(this.state));
  }

  render() {
    const fields = this.props.fields.map(field => (
      <InputField
        key={field}
        id={field}
        onChange={this.handleFieldChange}
        value={this.state[field]}
      />
    ));

    return (
      <div>
        <div>
          <button onClick={this.handleClick}>Click Me</button>
        </div>
        <div>
          {fields}
        </div>
      </div>
    );
  }
}

const App = () => {
  const fields = ["field1", "field2", "anotherField"];
  return <FormEditorParent fields={fields} />;
};

ReactDOM.render(<App/>, mountNode);

7
मुझे यकीन नहीं है कि मैं इसे उभार सकता हूं। आप यहाँ क्या उल्लेख करते हैं जो @ मार्कस-आईपी द्वारा पहले से ही उत्तर नहीं दिया गया है।
अलेक्जेंडर बर्ड

3

जैसा कि पिछले जवाबों में कहा गया है, राज्य को एक शीर्ष घटक में ले जाने की कोशिश करें और यह बच्चों को दिए गए कॉलबैक के माध्यम से राज्य को संशोधित करें।

उस स्थिति में जब आपको वास्तव में एक बच्चे की स्थिति तक पहुंचने की आवश्यकता होती है जिसे एक कार्यात्मक घटक (हुक) के रूप में घोषित किया जाता है जिसे आप मूल घटक में एक रेफरी घोषित कर सकते हैं, फिर इसे बच्चे को रेफरी विशेषता के रूप में पास करें लेकिन आपको React.forwardRef का उपयोग करने की आवश्यकता है और फिर हुक का उपयोग करेंइमपरेटिवहैंडल एक फ़ंक्शन घोषित करने के लिए जिसे आप मूल घटक में कॉल कर सकते हैं।

निम्नलिखित उदाहरण पर एक नज़र डालें:

const Parent = () => {
    const myRef = useRef();
    return <Child ref={myRef} />;
}

const Child = React.forwardRef((props, ref) => {
    const [myState, setMyState] = useState('This is my state!');
    useImperativeHandle(ref, () => ({getMyState: () => {return myState}}), [myState]);
})

तब आपको कॉल करके मूल घटक में myState प्राप्त करने में सक्षम होना चाहिए: myRef.current.getMyState();

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.