javascript شرح - تمرير الدعائم إلى المكون الرئيسي في React.js




ماهو ترجمة (6)

هل هناك طريقة بسيطة لتمرير props الطفل إلى والده باستخدام الأحداث ، في React.js؟

var Child = React.createClass({
  render: function() {
    <a onClick={this.props.onClick}>Click me</a>
  }
});

var Parent = React.createClass({
  onClick: function(event) {
    // event.component.props ?why is this not available?
  },
  render: function() {
    <Child onClick={this.onClick} />
  }
});

أعلم أنه يمكنك استخدام المكونات التي يتم التحكم فيها لتمرير قيمة المدخلات ، ولكن سيكون من الجيد تمرير المجموعة الكاملة n 'kaboodle. يحتوي المكون الفرعي أحيانًا على مجموعة من المعلومات التي لا تحتاج إلى البحث عنها.

ربما هناك طريقة لربط المكون بالحدث؟

تحديث - 9/1/2015

بعد استخدام React منذ أكثر من عام ، وحفزنا على إجابة سيباستيان لوربر ، انتهيت من تمرير مكونات الأطفال كمجالات إلى وظائف في الآباء ليست في الواقع طريقة رد الفعل ، ولم تكن فكرة جيدة على الإطلاق. لقد غيرت الجواب.


Answers

في ما يلي تنفيذ بسيط لـ 3 خطوات ES6 باستخدام ربط الدالة في منشئ الأهل. هذه هي الطريقة الأولى التي يوصي بها البرنامج التعليمي الرسمي (هناك أيضًا بنية لغوية في فئة الطبقة العامة لم يتم تغطيتها هنا). يمكنك العثور على جميع هذه المعلومات هنا https://reactjs.org/docs/handling-events.html

وظائف الوالدين الملزمة بحيث يمكن للأطفال الاتصال بهم (وتمرير البيانات حتى الأم!: D)

  1. تأكد من أن المُنشئ الأصل يقوم بربط الوظيفة التي قمت بإنشائها في الأصل
  2. تمرير الوظيفة المنحدرة إلى الطفل كدعم (لا لامدا لأننا نجتاز مرجعًا للعمل)
  3. استدعاء الدالة المندمجة من حدث الطفل (لامبدا! نحن نطلق على هذه الوظيفة عندما يتم إطلاق الحدث. إذا لم نفعل ذلك فسوف يتم تشغيل الوظيفة تلقائيًا عند التحميل ولن يتم تشغيلها في الحدث).

وظيفة الوالدين

handleFilterApply(filterVals){} 

منشئ الوالدين

this.handleFilterApply = this.handleFilterApply.bind(this);

الدعامة مرت على الطفل

onApplyClick = {this.handleFilterApply}

دعوة حدث الطفل

onClick = {() => {props.onApplyClick(filterVals)}

تحرير : راجع الأمثلة النهائية للأمثلة المحدثة ES6.

هذه الإجابة ببساطة تتعامل مع حالة العلاقة المباشرة بين الوالدين والطفل. عندما يحظى الوالد والطفل بالكثير من الوسطاء ، تحقق من هذه answer .

حلول أخرى تفتقد هذه النقطة

في حين أنها لا تزال تعمل بشكل جيد ، فإن الإجابات الأخرى تنقصها أهمية كبيرة.

هل هناك طريقة بسيطة لتمرير دعائم الطفل إلى والده باستخدام الأحداث ، في React.js؟

الوالد بالفعل لديه هذا الطفل الدعامة! : إذا كان الطفل لديه دعامة ، فذلك لأن والده قدم هذا الدعم للطفل! لماذا تريد أن يعيد الطفل الداعم إلى الوالد ، بينما من الواضح أن الوالد لديه بالفعل هذا الدعم؟

تنفيذ أفضل

الطفل : ليس بالضرورة أن يكون أكثر تعقيدًا من ذلك.

var Child = React.createClass({
  render: function () {
    return <button onClick={this.props.onClick}>{this.props.text}</button>;
  },
});

الوالد مع طفل واحد : باستخدام القيمة التي يمر بها للطفل

var Parent = React.createClass({
  getInitialState: function() {
     return {childText: "Click me! (parent prop)"};
  },
  render: function () {
    return (
      <Child onClick={this.handleChildClick} text={this.state.childText}/>
    );
  },
  handleChildClick: function(event) {
     // You can access the prop you pass to the children 
     // because you already have it! 
     // Here you have it in state but it could also be
     //  in props, coming from another parent.
     alert("The Child button text is: " + this.state.childText);
     // You can also access the target of the click here 
     // if you want to do some magic stuff
     alert("The Child HTML is: " + event.target.outerHTML);
  }
});

JsFiddle

الوالد مع قائمة بالأطفال : لا يزال لديك كل ما تحتاجه على الوالدين ولا تحتاج إلى جعل الطفل أكثر تعقيدًا.

var Parent = React.createClass({
  getInitialState: function() {
     return {childrenData: [
         {childText: "Click me 1!", childNumber: 1},
         {childText: "Click me 2!", childNumber: 2}
     ]};
  },
  render: function () {
    var children = this.state.childrenData.map(function(childData,childIndex) {
        return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>;
    }.bind(this));
    return <div>{children}</div>;
  },

  handleChildClick: function(childData,event) {
     alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
     alert("The Child HTML is: " + event.target.outerHTML);
  }
});

JsFiddle

من الممكن أيضًا استخدام this.handleChildClick.bind(null,childIndex) ، ثم استخدم this.state.childrenData[childIndex]

لاحظ أننا autobinding بسياق null لأنه بخلاف ذلك ، يصدر autobinding تحذيرًا يتعلق autobinding . استخدام null يعني أنك لا تريد تغيير سياق الوظيفة. انظر أيضا .

حول التغليف والاقتران في إجابات أخرى

هذا بالنسبة لي فكرة سيئة من حيث اقتران والتغليف:

var Parent = React.createClass({
  handleClick: function(childComponent) {
     // using childComponent.props
     // using childComponent.refs.button
     // or anything else using childComponent
  },
  render: function() {
    <Child onClick={this.handleClick} />
  }
});

استخدام الدعائم : كما أشرت أعلاه ، لديك بالفعل الدعائم في الوالد حتى لا يكون هناك فائدة من تمرير مكون الطفل بأكمله للوصول إلى الدعائم.

باستخدام الحذف : لديك بالفعل هدف النقر في الحدث ، وفي معظم الحالات هذا يكفي. بالإضافة إلى ذلك ، يمكنك استخدام ref مباشرة على الطفل:

<Child ref="theChild" .../>

والوصول إلى عقدة DOM في الأصل مع

React.findDOMNode(this.refs.theChild)

بالنسبة إلى الحالات الأكثر تقدمًا حيث تريد الوصول إلى حالات رفض متعددة للطفل في الأصل ، يمكن للطفل تمرير جميع عقد dom مباشرة في معاودة الاتصال.

يحتوي المكون على واجهة (props) ويجب على الوالد ألا يفترض أي شيء عن العمل الداخلي للطفل ، بما في ذلك هيكل DOM الداخلي أو عقد DOM التي يعلن عنها. ويعني الوالد الذي يستخدم مرجعًا للطفل أنك تدمج المكونين بإحكام.

لتوضيح المشكلة ، سآخذ هذا الاقتباس عن Shadow DOM ، الذي يتم استخدامه داخل المتصفحات لعرض أشياء مثل أشرطة التمرير ، أشرطة التمرير ، مشغلات الفيديو ...:

لقد أوجدوا حدودًا بين ما تستطيع مطورو الويب الوصول إليه وما يعتبر تفاصيل التنفيذ ، وبالتالي يتعذَّر الوصول إليك. ومع ذلك ، يمكن للمتصفح المرور عبر هذه الحدود حسب الإرادة. باستخدام هذه الحدود ، تمكنوا من بناء جميع عناصر HTML باستخدام نفس تقنيات الويب القديمة ، من خارج divs وامتدادات مثلما تفعل.

تكمن المشكلة في أنه إذا سمحت بتسريب تفاصيل تنفيذ الطفل إلى أحد الوالدين ، فستجعل من الصعب جدًا إعادة إنعاش الطفل دون التأثير على الوالد. هذا يعني كمؤلف مكتبة (أو كمحرر للمتصفح مع Shadow DOM) وهذا أمر خطير للغاية لأنك تسمح للعميل بالوصول إلى الكثير ، مما يجعل من الصعب جدًا ترقية الشفرة دون كسر التوافق.

إذا كان Chrome قد نفذ شريط التمرير الخاص به للسماح للعميل بالوصول إلى عقد DOM الداخلية لشريط التمرير هذا ، فهذا يعني أنه قد يكون لدى العميل إمكانية كسر شريط التمرير هذا ببساطة ، وأن التطبيقات ستنكسر بسهولة أكبر عندما يقوم Chrome بإجراء التحديث التلقائي بعد إعادة بيع شريط التمرير ... بدلاً من ذلك ، فإنها توفر الوصول إلى بعض الأشياء الآمنة فقط مثل تخصيص بعض أجزاء شريط التمرير باستخدام CSS.

حول استخدام أي شيء آخر

يمثل تمرير العنصر بأكمله في معاودة الاتصال خطراً وقد يؤدي إلى childComponent.setState(...) بأشياء غريبة مثل استدعاء childComponent.setState(...) أو childComponent.forceUpdate() أو تعيين متغيرات جديدة ، داخل الأصل ، مما يجعل التطبيق بأكمله من الصعب جدا التفكير فيه.

تحرير: أمثلة ES6

نظرًا لأن العديد من الأشخاص يستخدمون ES6 حاليًا ، في ما يلي الأمثلة نفسها لتركيب ES6

يمكن أن يكون الطفل بسيطًا جدًا:

const Child = ({
  onClick, 
  text
}) => (
  <button onClick={onClick}>
    {text}
  </button>
)

يمكن أن يكون الوالد إما فئة (ويمكنه في نهاية المطاف إدارة الحالة نفسها ، لكنني أقوم بتمريرها على شكل دعائم هنا:

class Parent1 extends React.Component {
  handleChildClick(childData,event) {
     alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
     alert("The Child HTML is: " + event.target.outerHTML);
  }
  render() {
    return (
      <div>
        {this.props.childrenData.map(child => (
          <Child
            key={child.childNumber}
            text={child.childText} 
            onClick={e => this.handleChildClick(child,e)}
          />
        ))}
      </div>
    );
  }
}

ولكن يمكن أيضًا تبسيطها إذا لم تكن بحاجة إلى إدارة الحالة:

const Parent2 = ({childrenData}) => (
  <div>
     {childrenData.map(child => (
       <Child
         key={child.childNumber}
         text={child.childText} 
         onClick={e => {
            alert("The Child button data is: " + child.childText + " - " + child.childNumber);
                    alert("The Child HTML is: " + e.target.outerHTML);
         }}
       />
     ))}
  </div>
)

JsFiddle

تحذير PERF (تطبيق على ES5 / ES6): إذا كنت تستخدم PureComponent أو shouldComponentUpdate ، فلن يتم تحسين التطبيقات أعلاه بشكل افتراضي نظرًا لاستخدام onClick={e => doSomething()} ، أو الربط مباشرةً أثناء مرحلة العرض ، نظرًا لأنه سيخلق وظيفة جديدة في كل مرة يتم عرض الأم. إذا كان هذا هو عنق الزجاجة الرائد في تطبيقك ، فيمكنك تمرير البيانات إلى الأطفال ، وإعادة إدخالها داخل رد الاتصال "المستقر" (تم تعيينه على الفئة الرئيسية ، وارتباطه this في مُنشئ الصف) بحيث يمكن الاستفادة من تحسين PureComponent ، أو يمكنك تنفيذ shouldComponentUpdate الخاصة بك وتجاهل معاودة الاتصال في التحقق مقارنة props.

يمكنك أيضًا استخدام مكتبة Recompose ، التي توفر مكونات عالية المستوى لتحقيق تحسينات فائقة الدقة:

// A component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}

// Optimized version of same component, using shallow comparison of props
// Same effect as React's PureRenderMixin
const OptimizedComponent = pure(ExpensiveComponent)

// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)

في هذه الحالة ، يمكنك تحسين مكون الطفل باستخدام:

const OptimizedChild = onlyUpdateForKeys(['text'])(Child)

في الأساس ، يمكنك استخدام الدعائم لإرسال المعلومات من وإلى الطفل والآباء والأمهات.

إضافة إلى كل الإجابات الرائعة ، دعوني أعطي مثالاً بسيطًا يشرح القيم المارة من الطفل إلى المكون الرئيسي في React

App.js

class App extends React.Component {
      constructor(){
            super();
            this.handleFilterUpdate = this.handleFilterUpdate.bind(this);
            this.state={name:'igi'}
      }
      handleFilterUpdate(filterValue) {
            this.setState({
                  name: filterValue
            });
      }
   render() {
      return (
        <div>
            <Header change={this.handleFilterUpdate} name={this.state.name} />
            <p>{this.state.name}</p>
        </div>
      );
   }
}

Header.js

class Header extends React.Component {
      constructor(){
            super();
            this.state={
                  names: 'jessy'
            }
      }
      Change(event) {

      // this.props.change(this.state.names);
      this.props.change('jessy');
  }

   render() {
      return (
       <button onClick={this.Change.bind(this)}>click</button>

      );
   }
}

Main.js

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App.jsx';

ReactDOM.render(<App />, document.getElementById('app'));

هذا هو ، الآن يمكنك تمرير القيم من العميل الخاص بك إلى الخادم.

ألقِ نظرة على وظيفة التغيير في Header.js

Change(event) {
      // this.props.change(this.state.names);
      this.props.change('jessy');
  }

هذه هي الطريقة التي دفع القيم في الدعائم من العميل إلى الملقم


التحديث (9/1/15): لقد جعل هذا البروتوكول سؤالاً قليلاً من هدف متحرك. لقد تم تحديثه مرة أخرى. لذا ، أشعر بالمسؤولية لتحديث ردي.

أولاً ، إجابة على المثال الذي قدمته:

نعم ، هذا ممكن.

يمكنك حل هذه المشكلة من خلال تحديث " onClick الخاص بالأطفال ليكون هذا this.props.onClick.bind(null, this) :

var Child = React.createClass({
  render: function () {
    return <a onClick={this.props.onClick.bind(null, this)}>Click me</a>;
  }
});

يمكن لمعالج الحدث في الوالد الخاص بك الوصول إلى المكون والحدث كما يلي:

  onClick: function (component, event) {
    // console.log(component, event);
  },

لقطة JSBin


لكن السؤال نفسه مضلل

الوالد يعرف بالفعل props الطفل.

هذا ليس واضحا في المثال المقدم لأنه يتم تقديم أي الدعائم في الواقع. قد يدعم نموذج التعليمة البرمجية هذا بشكل أفضل السؤال الذي يتم طرحه:

var Child = React.createClass({
  render: function () {
    return <a onClick={this.props.onClick}> {this.props.text} </a>;
  }
});

var Parent = React.createClass({
  getInitialState: function () {
    return { text: "Click here" };
  },
  onClick: function (event) {
    // event.component.props ?why is this not available? 
  },
  render: function() {
    return <Child onClick={this.onClick} text={this.state.text} />;
  }
});

يصبح أكثر وضوحا في هذا المثال أنك تعرف بالفعل ما هي الدعائم للطفل.

لقطة JSBin


إذا كان حقا حول استخدام الدعائم الطفل ...

إذا كان الأمر يتعلق حقًا باستخدام الدعائم الخاصة بالطفل ، فيمكنك تجنب أي اصطدام مع الطفل تمامًا.

يحتوي JSX على واجهة برمجة التطبيقات لبيانات الانتشار التي استخدمها غالبًا على مكونات مثل الطفل. يأخذ كل props ويطبقها على أحد المكونات. سيبدو الطفل هكذا:

var Child = React.createClass({
  render: function () {
    return <a {...this.props}> {this.props.text} </a>;
  }
});

يسمح لك باستخدام القيم مباشرة في الوالد:

var Parent = React.createClass({
  getInitialState: function () {
    return { text: "Click here" };
  },
  onClick: function (text) {
    alert(text);
  },
  render: function() {
    return <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />;
  }
});

لقطة JSBin


وليس هناك أي تكوين إضافي مطلوب لأنك تقوم بتوصيل مكونات إضافية للأطفال

var Parent = React.createClass({
  getInitialState: function () {
    return {
      text: "Click here",
      text2: "No, Click here",
    };
  },
  onClick: function (text) {
    alert(text);
  },
  render: function() {
    return <div>
      <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />
      <Child onClick={this.onClick.bind(null, this.state.text2)} text={this.state.text2} />
    </div>;
  }
});

لقطة JSBin

لكنني أشك في أن هذه ليست حالة الاستخدام الفعلية الخاصة بك. لذلك دعونا نحفر أبعد ...


مثال عملي قوي

من الصعب الحديث عن الطبيعة العامة للمثال المقدم. لقد أنشأت مكونًا يدل على الاستخدام العملي للسؤال أعلاه ، ويتم تنفيذه بطريقة رد الفعل للغاية:

DTServiceCalculator مثال على ذلك
DTServiceCalculator ريبو

هذا المكون هو آلة حاسبة بسيطة. تزودها بقائمة من الخدمات (مع الأسماء والأسعار) وستحسب إجمالي الأسعار المحددة.

الأطفال هم جاهلون بفرح

ServiceItem هو المكون ServiceItem في هذا المثال. ليس لديها الكثير من الآراء حول العالم الخارجي. يتطلب عددًا قليلًا من الدعائم ، أحدها دالة يجب استدعاؤها عند النقر عليها.

<div onClick={this.props.handleClick.bind(this.props.index)} />

لا يفعل أي شيء سوى استدعاء الاتصال handleClick المتوفر مع index المتوفر [ source ].

الآباء هم من الأطفال

DTServicesCalculator هو المكون الأصل هذا المثال. إنه أيضًا طفل دعونا ننظر.

ينشئ DTServiceCalculator قائمة DTServiceCalculator ( ServiceItem s) ويوفر لهم الدعائم [ source ]. إنه المكون الرئيسي لـ ServiceItem ولكنه العنصر التابع للمكون الذي يمررها في القائمة. لا يمتلك البيانات. لذلك يفوض مرة أخرى معالجة المكون إلى source الأصل المكون الخاص به

<ServiceItem chosen={chosen} index={i} key={id} price={price} name={name} onSelect={this.props.handleServiceItem} />

handleServiceItem بالتقاط المؤشر ، وتمريره من الطفل ، handleServiceItem إلى source الأصلي [ source ]

handleServiceClick (index) {
  this.props.onSelect(index);
}

المالكون يعرفون كل شيء

مفهوم "الملكية" هو مفهوم مهم في React. أوصي بقراءة المزيد حول هذا الموضوع here .

في المثال الذي عرضته ، أستمر في تفويض معالجة حدث ما إلى أعلى شجرة المكون حتى نصل إلى المكون الذي يملك الولاية.

عندما نصل في النهاية إلى هناك ، نتعامل مع اختيار الولاية / إلغاء الاختيار مثل ذلك [ source ]:

handleSelect (index) {
  let services = […this.state.services];
  services[index].chosen = (services[index].chosen) ? false : true;
  this.setState({ services: services });
}


استنتاج

جرِّب إبقاء معظم المكونات الخارجية غير شفافة قدر الإمكان. احرص على التأكد من أن لديهم تفضيلات قليلة للغاية حول كيفية اختيار أحد الوالدين لتطبيقها.

يرجى الانتباه إلى من يملك البيانات التي تتلاعب بها. في معظم الحالات ، ستحتاج إلى تفويض معالجة الحدث لأعلى الشجرة إلى المكون الذي يملك تلك الحالة.

جانبا: نمط تدفق هو وسيلة جيدة للحد من هذا النوع من هوكوب الضرورية في التطبيقات.


السؤال هو كيفية تمرير الحجة من الطفل إلى المكون الرئيسي. هذا المثال سهل الاستخدام والاختبار:

//Child component
class Child extends React.Component {
    render() {
        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div>
        )
    }
}

//Parent component
class Parent extends React.Component {
        constructor(props) {
        super(props);
        var handleToUpdate  = this.handleToUpdate.bind(this);
    }

        handleToUpdate(someArg){
            alert('We pass argument from Child to Parent: \n' + someArg);
    }

    render() {
        var handleToUpdate  =   this.handleToUpdate;
        return (<div>
                    <Child handleToUpdate = {handleToUpdate.bind(this)} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <Parent />,
        document.querySelector("#demo")
    );
}

انظر إلى JSFIDDLE


.دعم()

.prop( propertyName )
propertyName
Type: String
The name of the property to get.

تحصل الدالة .prop () على قيمة الخاصية للعنصر الأول فقط في المجموعة المتطابقة. تقوم بإرجاع قيمة غير محددة لخاصية لم يتم تعيينها ، أو إذا كانت المجموعة المطابقة لا تحتوي على عناصر. للحصول على قيمة لكل عنصر على حدة ، استخدم بنية looping مثل jQuery's .each () أو .map ().

<html>
<head>
  <meta charset="utf-8">
  <title>prop demo</title>
  <style>
  p {
    margin: 20px 0 0;
  }
  b {
    color: blue;
  }
  </style>
  <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>

<input id="check1" type="checkbox" checked="checked">
<label for="check1">Check me</label>
<p></p>

<script>
$( "input" ).change(function() {
  var $input = $( this );
  $( "p" ).html(
    ".attr( \"checked\" ): <b>" + $input.attr( "checked" ) + "</b><br>" +
    ".prop( \"checked\" ): <b>" + $input.prop( "checked" ) + "</b><br>" +
    ".is( \":checked\" ): <b>" + $input.is( ":checked" ) + "</b>" );
}).change();
</script>

</body>
</html>

.attr ()

.attr( attributeName )
attributeName
Type: String
The name of the attribute to get.

تحصل الدالة .attr () على قيمة السمة للعنصر الأول فقط في المجموعة المتطابقة. للحصول على قيمة لكل عنصر على حدة ، استخدم بنية looping مثل jQuery's .each () أو .map ().

استخدام طريقة .attr () jQuery للحصول على قيمة سمة عنصر له ميزتين رئيسيتين:

الملاءمة: يمكن الاتصال به مباشرة على كائن jQuery وتقييده بأساليب jQuery أخرى. التناسق عبر المتصفحات: يتم الإبلاغ عن قيم بعض السمات بشكل غير متسق عبر المتصفحات ، وحتى عبر إصدارات متصفح واحد. يقلل الأسلوب .attr () حالات عدم التناسق هذه.

<html>
<head>
  <meta charset="utf-8">
  <title>attr demo</title>
  <style>
  em {
    color: blue;
    font-weight: bold;
  }
  div {
    color: red;
  }
  </style>
  <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>

<p>Once there was a <em title="huge, gigantic">large</em> dinosaur...</p>

The title of the emphasis is:<div></div>

<script>
var title = $( "em" ).attr( "title" );
$( "div" ).text( title );
</script>

</body>
</html>






javascript reactjs