React.js: การห่อส่วนประกอบหนึ่งไปยังอีกองค์ประกอบ


187

ภาษาเทมเพลตจำนวนมากมีคำสั่ง "slot" หรือ "yield" ที่อนุญาตให้ทำการเรียงกลับกันบางส่วนของการควบคุมเพื่อห่อเทมเพลตหนึ่งไว้ในอีกอันหนึ่ง

เชิงมุมมี"transclude ตัวเลือก"

ทางรถไฟมีคำสั่งให้ผลผลิต หาก React.js มีข้อความให้ผลก็จะมีลักษณะเช่นนี้:

var Wrapper = React.createClass({
  render: function() {
    return (
      <div className="wrapper">
        before
          <yield/>
        after
      </div>
    );
  }
});

var Main = React.createClass({
  render: function() {
    return (
      <Wrapper><h1>content</h1></Wrapper>
    );
  }
});

ผลลัพธ์ที่ต้องการ:

<div class="wrapper">
  before
    <h1>content</h1>
  after
</div>

อนิจจา React.js <yield/>ไม่ได้ ฉันจะกำหนดองค์ประกอบ Wrapper เพื่อให้ได้ผลลัพธ์เดียวกันได้อย่างไร


คำตอบ:


246

ลอง:

var Wrapper = React.createClass({
  render: function() {
    return (
      <div className="wrapper">
        before
          {this.props.children}
        after
      </div>
    );
  }
});

ดูองค์ประกอบหลายรายการ: เด็กและประเภทของอุปกรณ์ประกอบฉากเด็กในเอกสารสำหรับข้อมูลเพิ่มเติม


8
หรือคุณสามารถใช้ส่วนประกอบการสั่งซื้อที่สูงขึ้น :) stackoverflow.com/a/31564812/82609
Sebastien Lorber

159

การใช้ children

const Wrapper = ({children}) => (
  <div>
    <div>header</div>
    <div>{children}</div>
    <div>footer</div>
  </div>
);

const App = ({name}) => <div>Hello {name}</div>;

const WrappedApp = ({name}) => (
  <Wrapper>
    <App name={name}/>
  </Wrapper>
);

render(<WrappedApp name="toto"/>,node);

เรื่องนี้เป็นที่รู้จักกันtransclusionในเชิงมุม

childrenเป็นเสาพิเศษในการตอบสนองและจะมีสิ่งที่อยู่ภายในแท็กองค์ประกอบของคุณ (ที่นี่<App name={name}/>ที่อยู่ภายในWrapperจึงเป็นchildren

โปรดทราบว่าคุณไม่จำเป็นต้องใช้childrenซึ่งเป็นเอกลักษณ์สำหรับส่วนประกอบและคุณสามารถใช้อุปกรณ์ประกอบฉากปกติได้เช่นกันหากคุณต้องการหรือผสมอุปกรณ์ประกอบฉากและเด็ก ๆ :

const AppLayout = ({header,footer,children}) => (
  <div className="app">
    <div className="header">{header}</div>
    <div className="body">{children}</div>
    <div className="footer">{footer}</div>
  </div>
);

const appElement = (
  <AppLayout 
    header={<div>header</div>}
    footer={<div>footer</div>}
  >
    <div>body</div>
  </AppLayout>
);

render(appElement,node);

ง่ายและดีสำหรับผู้ใช้หลายคนและฉันขอแนะนำให้ใช้กับแอพสำหรับผู้บริโภคส่วนใหญ่


แสดงผลอุปกรณ์ประกอบฉาก

เป็นไปได้ที่จะส่งผ่านฟังก์ชั่นการเรนเดอร์ไปยังส่วนประกอบรูปแบบนี้โดยทั่วไปเรียกว่าrender propและchildrenเสามักจะใช้เพื่อจัดเตรียมการเรียกกลับ

รูปแบบนี้ไม่ได้มีไว้สำหรับการจัดวางจริงๆ โดยทั่วไปแล้วองค์ประกอบของ wrapper จะใช้ในการควบคุมและจัดการสถานะและใช้ในการสร้างฟังก์ชั่น

ตัวอย่างเคาน์เตอร์:

const Counter = () => (
  <State initial={0}>
    {(val, set) => (
      <div onClick={() => set(val + 1)}>  
        clicked {val} times
      </div>
    )}
  </State>
); 

คุณสามารถได้รับจินตนาการมากขึ้นและให้วัตถุ

<Promise promise={somePromise}>
  {{
    loading: () => <div>...</div>,
    success: (data) => <div>{data.something}</div>,
    error: (e) => <div>{e.message}</div>,
  }}
</Promise>

หมายเหตุคุณไม่จำเป็นต้องใช้childrenมันเป็นเรื่องของรสนิยม / API

<Promise 
  promise={somePromise}
  renderLoading={() => <div>...</div>}
  renderSuccess={(data) => <div>{data.something}</div>}
  renderError={(e) => <div>{e.message}</div>}
/>

ณ วันนี้ห้องสมุดหลายแห่งกำลังใช้อุปกรณ์แสดงภาพ (บริบทการตอบโต้การตอบสนองการเคลื่อนไหวอพอลโล ... ) เพราะผู้คนมักจะพบว่า API นี้ง่ายกว่าของ HOC react-powerplugคือชุดขององค์ประกอบเรนเดอร์แบบง่าย ปฏิกิริยาตอบสนองที่ช่วยให้คุณทำองค์ประกอบ


ส่วนประกอบที่สูงกว่าการสั่งซื้อ (HOC)

const wrapHOC = (WrappedComponent) => {
  class Wrapper extends React.PureComponent {
    render() {
      return (
        <div>
          <div>header</div>
          <div><WrappedComponent {...this.props}/></div>
          <div>footer</div>
        </div>
      );
    }  
  }
  return Wrapper;
}

const App = ({name}) => <div>Hello {name}</div>;

const WrappedApp = wrapHOC(App);

render(<WrappedApp name="toto"/>,node);

สูงกว่าการสั่งซื้อชิ้นส่วน / HOCโดยทั่วไปคือฟังก์ชั่นที่ใช้องค์ประกอบและส่งกลับองค์ประกอบใหม่

การใช้ชิ้นส่วนที่สูงกว่าการสั่งซื้อสามารถ performant มากกว่าการใช้childrenหรือเพราะเสื้อคลุมจะมีความสามารถในการลัดวงจรการแสดงผลขั้นตอนหนึ่งไปข้างหน้าด้วยrender propsshouldComponentUpdate

PureComponentที่นี่เราจะใช้ เมื่อทำการเรนเดอร์แอปอีกครั้งหากWrappedAppชื่อ prop ไม่เปลี่ยนแปลงเมื่อเวลาผ่านไป wrapper มีความสามารถในการพูดว่า "ฉันไม่จำเป็นต้องเรนเดอร์เพราะอุปกรณ์ประกอบฉาก (อันที่จริงชื่อ) เหมือนกันก่อน" ด้วยchildrenวิธีการแก้ไขปัญหาข้างต้นแม้ว่าจะเป็น wrapper PureComponentมันก็ไม่ได้เป็นเพราะองค์ประกอบเด็กถูกสร้างขึ้นใหม่ทุกครั้งที่ผู้ปกครองแสดงผลซึ่งหมายความว่าเสื้อคลุมจะมีโอกาสแสดงผลอีกครั้งแม้ว่าองค์ประกอบที่ห่อจะบริสุทธิ์ มีปลั๊กอิน babelที่สามารถช่วยลดปัญหานี้และให้แน่ใจว่าchildrenองค์ประกอบคงที่เมื่อเวลาผ่านไป


ข้อสรุป

ส่วนประกอบที่สูงกว่าสามารถให้ประสิทธิภาพที่ดีขึ้นแก่คุณ มันไม่ซับซ้อนนัก แต่มันดูไม่เป็นมิตรในตอนแรก

อย่าโอนย้ายฐานข้อมูลทั้งหมดของคุณไปที่ HOC หลังจากอ่านบทความนี้ เพียงจำไว้ว่าบนเส้นทางที่สำคัญของแอพของคุณคุณอาจต้องการใช้ HOC แทนการใช้โปรแกรมเสริมด้วยเหตุผลด้านประสิทธิภาพโดยเฉพาะอย่างยิ่งหากใช้ wrapper เดียวกันหลายครั้งก็คุ้มค่าที่จะพิจารณาให้เป็น HOC

Redux ใช้ในตอนแรก wrapper รันไทม์<Connect>และเปลี่ยนในภายหลังเพื่อ HOC connect(options)(Comp)ด้วยเหตุผลด้านประสิทธิภาพ (โดยค่าเริ่มต้น wrapper นั้นบริสุทธิ์และใช้งานshouldComponentUpdate) นี่เป็นภาพประกอบที่สมบูรณ์แบบของสิ่งที่ฉันต้องการเน้นในคำตอบนี้

หมายเหตุหากองค์ประกอบมี API ของ render-prop โดยทั่วไปแล้วจะสร้าง HOC ให้ง่ายกว่านั้นดังนั้นหากคุณเป็นผู้แต่ง lib คุณควรเขียน render prop API ก่อนและสุดท้ายให้เป็นรุ่น HOC นี่คือสิ่งที่ Apollo ทำกับ<Query>ส่วนประกอบ render-prop และgraphqlHOC ใช้มัน

โดยส่วนตัวแล้วฉันใช้ทั้งคู่ แต่เมื่อสงสัยฉันก็ชอบ HOC เพราะ:

  • มันเป็นสำนวนที่ยิ่งใหญ่กว่าที่จะเขียน ( compose(hoc1,hoc2)(Comp)) เปรียบเทียบกับการแสดงประกอบฉาก
  • มันสามารถทำให้การแสดงของฉันดีขึ้น
  • ฉันคุ้นเคยกับการเขียนโปรแกรมแบบนี้

ฉันไม่ลังเลที่จะใช้ / สร้างเครื่องมือโปรดของฉันในเวอร์ชัน HOC:

  • Context.Consumerคอมพ์ของ React
  • อันเป็นของ Subscribe
  • ใช้graphqlHOC ของ Apollo แทนQueryrender prop

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


1
วิธีการนี้ยังช่วยให้ง่ายต่อการส่งผ่านอุปกรณ์ประกอบฉากลงในส่วนของเด็ก (ในกรณีนี้คือ Hello) จาก React 0.14. * เป็นต้นไปวิธีเดียวที่จะส่งอุปกรณ์ประกอบฉากไปยังองค์ประกอบสำหรับเด็กคือการใช้ React.createClone ซึ่งอาจมีราคาแพง
Mukesh Soni

2
คำถาม: คำตอบระบุว่า "ประสิทธิภาพที่ดีกว่า" - สิ่งที่ฉันไม่เข้าใจ: ดีกว่าเมื่อเทียบกับโซลูชันอื่น
ฟิลิปป์

1
HOCs สามารถมีประสิทธิภาพที่ดีกว่าเมื่อเทียบกับ runtime wrapper เนื่องจากสามารถลัดวงจรการเรนเดอร์ก่อนหน้านี้
Sebastien Lorber

1
ขอบคุณ! มันเหมือนกับคุณเอาคำจากเดือนของฉัน แต่คุณแสดงให้พวกเขาด้วยความสามารถที่มากขึ้น👍
MacKentoch

1
นี่เป็นคำตอบที่ดีกว่ามาก:] ขอบคุณ!
cullanrocks

31

นอกเหนือจากคำตอบของโซฟีแล้วฉันยังพบว่ามีประโยชน์ในการส่งประเภทองค์ประกอบลูก ๆ โดยทำสิ่งนี้:

var ListView = React.createClass({
    render: function() {
        var items = this.props.data.map(function(item) {
            return this.props.delegate({data:item});
        }.bind(this));
        return <ul>{items}</ul>;
    }
});

var ItemDelegate = React.createClass({
    render: function() {
        return <li>{this.props.data}</li>
    }
});

var Wrapper = React.createClass({    
    render: function() {
        return <ListView delegate={ItemDelegate} data={someListOfData} />
    }
});

2
ฉันไม่เห็นเอกสารใด ๆ เกี่ยวกับdelegateคุณพบได้อย่างไร
NVI

4
คุณสามารถเพิ่มอุปกรณ์ประกอบฉากอะไรก็ได้ที่คุณต้องการให้เป็นส่วนประกอบและตั้งชื่อตามที่คุณต้องการฉันใช้ this.props.delegate ที่แถว 4 แต่อาจตั้งชื่ออย่างอื่นได้
krs
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.