ส่งอุปกรณ์ประกอบฉากไปยังองค์ประกอบหลักใน React.js


295

ไม่มีวิธีง่ายๆในการส่งเด็ก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} />
  }
});

ฉันรู้ว่าคุณสามารถใช้ส่วนประกอบที่ควบคุมได้เพื่อส่งผ่านค่าอินพุต แต่มันก็ดีที่จะผ่านชุดคิทพูนทั้งหมด บางครั้งองค์ประกอบย่อยมีชุดข้อมูลที่คุณไม่ต้องการค้นหา

อาจมีวิธีผูกองค์ประกอบกับกิจกรรมหรือไม่

อัปเดต - 9/1/2558

หลังจากใช้ React มานานกว่าหนึ่งปีและกระตุ้นโดยคำตอบของ Sebastien Lorber ฉันได้สรุปว่าการส่งผ่านองค์ประกอบของเด็กเป็นข้อโต้แย้งเกี่ยวกับหน้าที่ของผู้ปกครองไม่ใช่ความจริงในทางของ React และไม่เคยเป็นความคิดที่ดีเลย ฉันเปลี่ยนคำตอบแล้ว


มีคำตอบต่าง ๆ สำหรับปัญหาที่คล้ายกัน ( ที่นี่และที่นี่ ) แต่ไม่มีพวกเขาดูเหมือนสง่างามโดยเฉพาะอย่างยิ่ง
Bojangles

ฉันเห็นด้วย - การส่งผ่านเหตุการณ์ที่เกิดขึ้นในโซ่นั้นยอดเยี่ยม แต่มันยอดเยี่ยมมากที่จะรู้ว่าองค์ประกอบใดที่มาจากเหตุการณ์
KendallB

โปรดดูคำตอบของฉันเพราะฉันคิดว่าคำตอบที่ยอมรับนั้นไม่ดีพอ stackoverflow.com/a/31756470/82609
Sebastien Lorber

1
kit n 'kaboodle - รวมทุกอย่างเข้าไว้ด้วยกัน เพื่อรับระฆังและเสียงนกหวีดทั้งหมด Combonation urbandictionary.com/define.php?term=kit%20and%20caboodle
Filip Bartuzi

คำตอบ:


268

แก้ไข : ดูตัวอย่างสิ้นสุดสำหรับตัวอย่างที่อัปเดต ES6

คำตอบนี้เพียงจัดการกรณีของความสัมพันธ์พ่อแม่และลูกโดยตรง เมื่อผู้ปกครองและเด็กมีแนวโน้มที่จะเป็นคนกลางให้ตรวจสอบคำตอบนี้นี้

โซลูชันอื่น ๆ ขาดประเด็น

ในขณะที่พวกเขายังทำงานได้ดีคำตอบอื่น ๆ จะหายไปบางสิ่งที่สำคัญมาก

ไม่มีวิธีง่ายๆในการส่งอุปกรณ์ประกอบฉากของเด็กไปยังผู้ปกครองโดยใช้กิจกรรมใน React.js หรือไม่?

ผู้ปกครองมีลูกค้ำยันแล้ว! : ถ้าเด็กมีเสาแสดงว่ามันเป็นเพราะผู้ปกครองให้เสานั้นกับเด็ก! ทำไมคุณถึงต้องการให้เด็กส่งเสาไปยังผู้ปกครองในขณะที่ผู้ปกครองมีเสานั้นอยู่แล้ว?

การใช้งานที่ดีขึ้น

เด็ก : ไม่ต้องซับซ้อนกว่านั้นอีกแล้ว

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

Parent with child child : การใช้ค่าที่ส่งผ่านไปยัง child

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]

โปรดทราบว่าเรามีผลผูกพันกับnullบริบทเนื่องจากไม่เช่นนั้นจะทำซ้ำการเตือนที่เกี่ยวข้องกับระบบการโต้กลับอัตโนมัติ ใช้ 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} />
  }
});

การใช้อุปกรณ์ประกอบฉาก : ตามที่ฉันได้อธิบายไว้ข้างต้นคุณมีอุปกรณ์ประกอบฉากในตัวแม่อยู่แล้วดังนั้นจึงไม่มีประโยชน์ที่จะส่งอุปกรณ์ประกอบทั้งหมดของเด็กเพื่อเข้าถึงอุปกรณ์ประกอบฉาก

การใช้ผู้อ้างอิง : คุณมีเป้าหมายการคลิกอยู่แล้วในเหตุการณ์และในกรณีนี้ก็เพียงพอแล้ว นอกจากนี้คุณสามารถใช้การอ้างอิงกับเด็กได้โดยตรง:

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

และเข้าถึงโหนด DOM ในพาเรนต์ด้วย

React.findDOMNode(this.refs.theChild)

สำหรับกรณีขั้นสูงที่คุณต้องการเข้าถึงการอ้างอิงหลายรายการของเด็กในพาเรนต์เด็กสามารถส่งผ่านโหนด dom ทั้งหมดในการเรียกกลับโดยตรง

ส่วนประกอบมีอินเทอร์เฟซ (อุปกรณ์ประกอบฉาก) และพาเรนต์ไม่ควรสมมติสิ่งใด ๆ เกี่ยวกับการทำงานภายในของเด็กรวมถึงโครงสร้าง DOM ภายในหรือโหนด DOM ที่ประกาศการอ้างอิง ผู้ปกครองที่ใช้การอ้างอิงของเด็กหมายความว่าคุณจับคู่คอมโพเนนต์ทั้งสองไว้แน่น

เพื่อแสดงให้เห็นถึงปัญหาฉันจะใช้คำพูดนี้เกี่ยวกับShadow DOMที่ใช้ภายในเบราว์เซอร์เพื่อแสดงสิ่งต่าง ๆ เช่นแถบเลื่อนแถบเลื่อนเครื่องเล่นวิดีโอ ... :

พวกเขาสร้างขอบเขตระหว่างสิ่งที่คุณผู้พัฒนาเว็บสามารถเข้าถึงและสิ่งที่ถือว่าเป็นรายละเอียดการใช้งานจึงไม่สามารถเข้าถึงคุณได้ อย่างไรก็ตามเบราว์เซอร์สามารถข้ามขอบเขตนี้ได้ตามต้องการ ด้วยขอบเขตนี้พวกเขาจึงสามารถสร้างองค์ประกอบ HTML ทั้งหมดโดยใช้เทคโนโลยีเว็บแบบเก่าที่ดีออกไปจาก div และ spans เหมือนกับที่คุณต้องการ

ปัญหาคือว่าถ้าคุณปล่อยให้รายละเอียดการใช้งานของเด็กรั่วไหลไปยังผู้ปกครองคุณทำให้ยากที่จะ refactor เด็กโดยไม่ส่งผลกระทบต่อผู้ปกครอง ซึ่งหมายความว่าในฐานะผู้เขียนไลบรารี (หรือในฐานะผู้แก้ไขเบราว์เซอร์ที่มี Shadow DOM) สิ่งนี้เป็นสิ่งที่อันตรายมากเพราะคุณปล่อยให้ไคลเอ็นต์เข้าถึงมากเกินไปทำให้ยากต่อการอัพเกรดรหัสโดยไม่ทำลายความเข้ากันได้ย้อนหลัง

หาก Chrome ได้นำแถบเลื่อนไปใช้เพื่อให้ไคลเอ็นต์เข้าถึงโหนด dom ด้านในของแถบเลื่อนนั้นหมายความว่าไคลเอ็นต์อาจมีความเป็นไปได้ที่จะทำลายแถบเลื่อนนั้นและแอปนั้นจะแตกง่ายขึ้นเมื่อ Chrome ทำการอัปเดตอัตโนมัติหลังจากทำการเปลี่ยนใหม่ แถบเลื่อน ... แต่พวกเขาให้การเข้าถึงสิ่งที่ปลอดภัยบางอย่างเท่านั้นเช่นการปรับแต่งบางส่วนของแถบเลื่อนด้วย CSS

เกี่ยวกับการใช้อย่างอื่น

ผ่านองค์ประกอบทั้งหมดในการเรียกกลับเป็นอันตรายและอาจนำไปสู่การพัฒนาสามเณรที่จะทำสิ่งที่แปลกมาก ๆ เช่นการโทร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 WARNING (นำไปใช้กับ ES5 / ES6): หากคุณกำลังใช้งานPureComponentหรือshouldComponentUpdateการใช้งานด้านบนจะไม่ได้รับการปรับให้เหมาะสมโดยค่าเริ่มต้นเนื่องจากการใช้onClick={e => doSomething()}หรือการผูกโดยตรงระหว่างขั้นตอนการเรนเดอร์เพราะมันจะสร้างฟังก์ชันใหม่ทุกครั้ง หากเป็นคอขวด perf ใน app ของคุณคุณสามารถส่งผ่านข้อมูลไปยังเด็กและ reinject มันภายใน "คงที่" โทรกลับ (ตั้งอยู่บนชั้นผู้ปกครองและ binded ไปthisในตัวสร้าง class) เพื่อให้PureComponentการเพิ่มประสิทธิภาพสามารถเตะในหรือคุณ สามารถใช้ของคุณเองshouldComponentUpdateและละเว้นการโทรกลับในการตรวจสอบการเปรียบเทียบอุปกรณ์ประกอบฉาก

นอกจากนี้คุณยังสามารถใช้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)

1
ใช่หลังจากใช้ปีที่แล้วด้วยการใช้ React ฉันเห็นด้วย - ไม่มีเหตุผลที่ดีที่จะส่งชิ้นส่วนที่มีการสร้างอินสแตนซ์ผ่านฟังก์ชั่น ฉันจะคัดค้านคำตอบของฉันเอง ฉันยินดีที่จะเปลี่ยนคำตอบด้วยการปรับเปลี่ยนบางอย่าง: "การใช้งานที่ดีขึ้น" ของคุณไม่ได้ตอบคำถามจริงที่เกิดขึ้นซึ่งก็คือ "ฉันจะรู้ได้อย่างไรว่าลูกคนไหนที่ถูกเลือกในกลุ่ม?" ฟลักซ์เป็นคำตอบสำหรับการใด ๆ ของแอปดีขนาด แต่ในแอปเล็ก ๆ ผมก็ยินดีที่จะส่งผ่านค่า (ไม่ใช่ส่วนประกอบ) propsกลับขึ้นห่วงโซ่การโทรผ่านฟังก์ชั่นบน ตกลง?
KendallB

@KendallB ดีใจที่คุณเห็นด้วยกับคำตอบของฉัน :) How do I know which of my children was chosen among a group?นี่ไม่ใช่ส่วนหนึ่งของคำถามเดิม แต่ฉันรวมคำตอบไว้ในคำตอบแล้ว แตกต่างจากการแก้ไขของคุณฉันคิดว่าไม่จำเป็นต้องปรับเปลี่ยนเด็กเลยแม้แต่เมื่อต้องรับมือกับรายการเนื่องจากคุณสามารถใช้bindโดยตรงในพาเรนต์
Sebastien Lorber

ฉันแก้ไขคำตอบของคุณส่วนที่เกี่ยวกับการอ้างอิง omerwazir.com/posts/react-getdomnode-replaced-with-findDOMNode
ffxsam

สวัสดี @SebastienLorber ฉันมาจากอนาคต เกี่ยวกับสายนี้onClick={this.handleChildClick.bind(null,childData)}ที่คุณกล่าวถึงเกี่ยวกับautobindingนี่คือยังคงใช้บังคับในขณะนี้ที่ตอบสนองยังไม่ได้รวมอยู่ในรูปแบบการเรียน ฉันอ่านสิ่งต่าง ๆ มากมาย แต่ตอนนี้ฉันยังสับสนอยู่ ฉันใช้ ES6 class modelอยู่แล้วดังนั้นฉันจึงเปลี่ยนnullเป็นthisและรหัสของฉันก็ยังใช้งานได้ คุณจะแปลส่วนนั้นของรหัสของคุณเป็นสไตล์ ES6 อย่างไร
JohnnyQ

ฉันยังดิ้นรนเพื่อให้ได้ผลตอบสนองล่าสุดสิ่งที่ต้องเปลี่ยน
Hussein Duvigneau

143

Update (9/1/15): OP ได้ทำให้คำถามนี้เป็นเป้าหมายที่เคลื่อนไหวเล็กน้อย มันได้รับการปรับปรุงอีกครั้ง ดังนั้นฉันรู้สึกรับผิดชอบในการอัปเดตคำตอบของฉัน

ขั้นแรกให้คำตอบสำหรับตัวอย่างที่คุณให้ไว้:

ใช่มันเป็นไปได้

คุณสามารถแก้ปัญหานี้ได้โดยอัปเดต Child's 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 snapshot


แต่คำถามนั้นทำให้เข้าใจผิด

ผู้ปกครองรู้จักเด็ก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 snapshot


ถ้ามันเกี่ยวกับการใช้อุปกรณ์ประกอบฉากของเด็กอย่างแท้จริง ...

ถ้ามันเกี่ยวกับการใช้อุปกรณ์ประกอบฉากของเด็กอย่างแท้จริงคุณสามารถหลีกเลี่ยงการเชื่อมต่อของเด็กด้วยกันได้

JSX มีAPI คุณลักษณะการแพร่กระจายที่ฉันมักจะใช้กับส่วนประกอบเช่นลูก มันใช้เวลาทั้งหมด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 snapshot


และไม่จำเป็นต้องมีการกำหนดค่าเพิ่มเติมในขณะที่คุณเชื่อมต่อคอมโพเนนต์ย่อยเพิ่มเติม

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 snapshot

แต่ฉันคิดว่านั่นไม่ใช่กรณีการใช้งานจริงของคุณ ดังนั้นเรามาขุดเพิ่มเติม ...


ตัวอย่างการปฏิบัติที่แข็งแกร่ง

ลักษณะทั่วไปของตัวอย่างที่ให้มานั้นยากที่จะพูดถึง ฉันได้สร้างส่วนประกอบที่แสดงให้เห็นถึงการใช้งานจริงสำหรับคำถามข้างต้นได้รับการใช้งานด้วยวิธีตอบสนองที่ดีมาก:

DTServiceCalculator ตัวอย่างการทำงาน
DTServiceCalculator repo

ส่วนประกอบนี้เป็นเครื่องคิดเลขบริการแบบง่าย คุณให้รายการบริการ (พร้อมชื่อและราคา) และจะคำนวณยอดรวมของราคาที่เลือก

เด็กเขลาอย่างไม่รู้ตัว

ServiceItemเป็นองค์ประกอบของเด็กในตัวอย่างนี้ มันไม่ได้มีความคิดเห็นมากมายเกี่ยวกับโลกภายนอก มันต้องมีอุปกรณ์ประกอบฉากไม่กี่อย่างหนึ่งในนั้นคือฟังก์ชั่นที่จะเรียกเมื่อคลิก

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

มันไม่ทำอะไรนอกจากเรียกการhandleClickโทรกลับที่ให้มาด้วยindex[ แหล่งที่มา ] ที่ให้ไว้

พ่อแม่เป็นลูก

DTServicesCalculatorเป็นองค์ประกอบหลักคือตัวอย่างนี้ นอกจากนี้ยังเป็นเด็ก มาดูกัน

DTServiceCalculatorสร้างรายการองค์ประกอบย่อยServiceItemและจัดเตรียมอุปกรณ์ประกอบฉาก [ แหล่งที่มา ] มันเป็นส่วนประกอบหลักของServiceItemมัน แต่เป็นองค์ประกอบย่อยขององค์ประกอบที่ผ่านรายการ มันไม่ได้เป็นเจ้าของข้อมูล ดังนั้นจึงมอบหมายการจัดการส่วนประกอบให้กับแหล่งที่มาขององค์ประกอบหลักอีกครั้ง

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

handleServiceItemจับดัชนีส่งผ่านจากเด็กและมอบให้กับผู้ปกครอง [ แหล่งที่มา ]

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

เจ้าของรู้ทุกอย่าง

แนวคิดของ“ ความเป็นเจ้าของ” เป็นสิ่งสำคัญในการตอบสนอง ฉันแนะนำให้อ่านเพิ่มเติมเกี่ยวกับที่นี่นี่

ในตัวอย่างที่ฉันแสดงฉันยังคงมอบหมายการจัดการเหตุการณ์ขึ้นโครงสร้างส่วนประกอบจนกว่าเราจะไปถึงองค์ประกอบที่เป็นเจ้าของรัฐ

ในที่สุดเมื่อเราไปถึงที่นั่นเราจัดการกับการเลือกของรัฐ / การเลือกอย่างเช่น [แหล่งที่มา ]:

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


ข้อสรุป

ลองทำให้ส่วนประกอบนอกสุดของคุณทึบที่สุดเท่าที่จะทำได้ มุ่งมั่นเพื่อให้แน่ใจว่าพวกเขามีการตั้งค่าน้อยมากเกี่ยวกับวิธีที่องค์ประกอบแม่อาจเลือกที่จะใช้พวกเขา

โปรดระวังว่าใครเป็นเจ้าของข้อมูลที่คุณกำลังจัดการ ในกรณีส่วนใหญ่คุณจะต้องมอบหมายกิจกรรมการจัดการต้นไม้ให้กับองค์ประกอบที่เป็นเจ้าของสถานะนั้น

นอกเหนือ: รูปแบบ Fluxเป็นวิธีที่ดีในการลดการเชื่อมต่อของส่วนต่างๆที่จำเป็นในแอพ


ขอบคุณสำหรับคำตอบที่กระชับมันช่วยให้ฉันเข้าใจทำปฏิกิริยาได้ดีขึ้น! ฉันมีคำถามตามสายเดียวกัน ฉันกำลังใช้ Flux สำหรับ pub / sub แทนที่จะส่งตัวจัดการเหตุการณ์เด็กไปยังผู้ปกครองเหมือนกับตัวอย่างของคุณคุณสามารถใช้สิ่งนี้เป็น 'การกระทำ' และฟังได้ คุณคิดว่านี่เป็นทางเลือกที่ดีและใช้ Flux หรือไม่?
Pathsofdesign

1
ขอบคุณ @Pathsofdesign! มันจะขึ้นอยู่กับ Flux มีแนวคิดของ Controller-Views นี้ ในตัวอย่างนี้Parentอาจเป็นเช่น Controller-View ในขณะที่Childเป็นเพียง dumb-View (คอมโพเนนต์) เฉพาะมุมมองคอนโทรลเลอร์เท่านั้นที่ควรมีความรู้เกี่ยวกับแอปพลิเคชัน ในกรณีนี้คุณยังจะผ่านการดำเนินการจากParentการChildเป็นเสา เนื่องจาก Action มีความเกี่ยวข้อง Flux จึงมีรูปแบบที่กำหนดไว้อย่างชัดเจนสำหรับการโต้ตอบระหว่าง Actions เพื่ออัปเดต Views facebook.github.io/flux/docs/…
chantastic

1
onClick={this.onClick.bind(null, this.state.text)}ไม่จำเป็นต้องผูกสถานะเป็นพาเรนต์ ดังนั้นonClick={this.onClick}จะทำงาน ที่ใดonClick = ()=>{const text = this.state.text;..}ในแม่
Shishir Arora

25

ดูเหมือนว่ามีคำตอบง่ายๆ พิจารณาสิ่งนี้:

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

var Parent = React.createClass({
  onClick: function(component, event) {
    component.props // #=> {Object...}
  },
  render: function() {
    <Child onClick={this.onClick} />
  }
});

คีย์กำลังเรียกbind(null, this)ใช้this.props.onClickเหตุการณ์ส่งผ่านจากพาเรนต์ ตอนนี้ฟังก์ชั่น onClick ยอมรับข้อโต้แย้งและcomponent eventฉันคิดว่านั่นคือสิ่งที่ดีที่สุดในโลก

อัปเดต: 9/1/2558

นี่เป็นความคิดที่ไม่ดี: การให้รายละเอียดการใช้งานของเด็กรั่วไหลไปยังผู้ปกครองนั้นไม่ใช่เส้นทางที่ดี ดูคำตอบของ Sebastien Lorber


arent ข้อโต้แย้งในทางที่ผิด (สถานที่เปลี่ยน)?
Surrican

1
@TheSurrican ไม่เป็นไรดี อาร์กิวเมนต์แรกถูกผูกไว้กับthisภายในฟังก์ชัน (ซึ่งไม่จำเป็นต้องมีอยู่แล้ว) และอาร์กิวเมนต์ที่สองจะถูกจัดเตรียมไว้เป็นอาร์กิวเมนต์แรกเมื่อเรียกใช้ onClick
manmal

13

คำถามคือวิธีการผ่านการโต้แย้งจากเด็กไปยังองค์ประกอบหลัก ตัวอย่างนี้ใช้งานง่ายและผ่านการทดสอบ:

//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


ในกรณีของฉันมันลืมฟังก์ชั่นการโทรกลับเข้าไปใน click ()
Long Nguyen

6

โดยทั่วไปคุณใช้อุปกรณ์ประกอบฉากในการส่งข้อมูลไปและกลับจากเด็กและผู้ปกครอง

การเพิ่มคำตอบที่ยอดเยี่ยมทั้งหมดให้ฉันยกตัวอย่างง่ายๆที่อธิบายค่าผ่านจากเด็กไปยังองค์ประกอบหลักใน 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'));

นั่นคือตอนนี้คุณสามารถส่งผ่านค่าจากลูกค้าของคุณไปยังเซิร์ฟเวอร์

ดูฟังก์ชั่นเปลี่ยนในส่วนหัว

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

นี่คือวิธีที่คุณส่งค่าไปยังอุปกรณ์ประกอบฉากจากไคลเอนต์ไปยังเซิร์ฟเวอร์


ฉันขอทราบได้ไหมว่านี่เป็นวิธีที่คุณผลักดันคุณค่าจากอุปกรณ์ประกอบฉากจากลูกค้าไปยังเซิร์ฟเวอร์คุณหมายถึงอะไรโดยแถลงการณ์นี้
G1P

2

นี่คือการประยุกต์ใช้ ES6 แบบ 3 ขั้นตอนง่ายๆโดยใช้การเชื่อมฟังก์ชันในตัวสร้างหลัก นี่เป็นวิธีแรกในการแนะนำการตอบสนองต่อการกวดวิชาอย่างเป็นทางการ (นอกจากนี้ยังมีไวยากรณ์ฟิลด์คลาสสาธารณะที่ไม่ครอบคลุมที่นี่) คุณสามารถค้นหาข้อมูลทั้งหมดได้ที่นี่https://reactjs.org/docs/handling-events.html

ฟังก์ชั่นเชื่อมโยงผู้ปกครองเพื่อให้เด็กสามารถโทรหาพวกเขา (และส่งผ่านข้อมูลได้ถึงผู้ปกครอง!: D)

  1. ตรวจสอบให้แน่ใจใน constructor หลักที่คุณผูกฟังก์ชั่นที่คุณสร้างใน parent
  2. ผ่านฟังก์ชั่นที่ถูกผูกไว้ลงไปที่เด็กเพื่อเป็นเสา (ไม่มีแลมบ์ดาเพราะเราผ่านการอ้างอิงไปยังฟังก์ชั่น)
  3. เรียกฟังก์ชั่นที่ถูกผูกไว้จากเหตุการณ์ลูก (แลมบ์ดาเรากำลังเรียกฟังก์ชั่นเมื่อมีการยิงเหตุการณ์ถ้าเราไม่ทำเช่นนี้ฟังก์ชั่นจะทำงานโดยอัตโนมัติเมื่อโหลดและไม่ถูกเรียกใช้ในเหตุการณ์)

ฟังก์ชั่นผู้ปกครอง

handleFilterApply(filterVals){} 

ตัวสร้างผู้ปกครอง

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

เสาส่งถึงเด็ก

onApplyClick = {this.handleFilterApply}

การเรียกเหตุการณ์เด็ก

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

0

นี่คือตัวอย่างโดยไม่ใช้เหตุการณ์ onClick ฉันเพียงแค่ส่งฟังก์ชั่นการโทรกลับให้เด็กด้วยอุปกรณ์ประกอบฉาก ด้วยการโทรกลับการโทรแบบลูกก็จะส่งข้อมูลกลับมาด้วย ฉันได้รับแรงบันดาลใจจากตัวอย่างในเอกสารเอกสาร

ตัวอย่างเล็ก ๆ (นี่คือไฟล์ tsx ดังนั้นอุปกรณ์และสถานะต้องถูกประกาศอย่างสมบูรณ์ฉันลบตรรกะบางอย่างออกจากส่วนประกอบดังนั้นจึงเป็นรหัสน้อยกว่า)

* อัปเดต: สิ่งสำคัญคือการเชื่อมโยงสิ่งนี้กับการติดต่อกลับมิฉะนั้นการโทรกลับจะมีขอบเขตของลูกไม่ใช่ผู้ปกครอง ปัญหาเท่านั้น: มันเป็นแม่ "เก่า" ...

SymptomChoser เป็นพาเรนต์:

interface SymptomChooserState {
  // true when a symptom was pressed can now add more detail
  isInDetailMode: boolean
  // since when user has this symptoms
  sinceDate: Date,
}

class SymptomChooser extends Component<{}, SymptomChooserState> {

  state = {
    isInDetailMode: false,
    sinceDate: new Date()
  }

  helloParent(symptom: Symptom) {
    console.log("This is parent of: ", symptom.props.name);
    // TODO enable detail mode
  }

  render() {
    return (
      <View>
        <Symptom name='Fieber' callback={this.helloParent.bind(this)} />
      </View>
    );
  }
}

อาการเป็นเด็ก (ในอุปกรณ์ประกอบฉากของเด็กฉันประกาศฟังก์ชั่นการโทรกลับในฟังก์ชั่นที่เลือกอาการโทรกลับถูกเรียก):

interface SymptomProps {
  // name of the symptom
  name: string,
  // callback to notify SymptomChooser about selected Symptom.
  callback: (symptom: Symptom) => void
}

class Symptom extends Component<SymptomProps, SymptomState>{

  state = {
    isSelected: false,
    severity: 0
  }

  selectedSymptom() {
    this.setState({ isSelected: true });
    this.props.callback(this);
  }

  render() {
    return (
      // symptom is not selected
      <Button
        style={[AppStyle.button]}
        onPress={this.selectedSymptom.bind(this)}>
        <Text style={[AppStyle.textButton]}>{this.props.name}</Text>
      </Button>
    );
  }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.