React.js: Bir bileşeni diğerine sarma


187

Birçok şablon dili, bir şablonu diğerinin içine sarmak için bir çeşit kontrolün tersine çevrilmesine izin veren "yuvalar" veya "verim" ifadelerine sahiptir.

Açısal "kopyalama" seçeneği vardır .

Raylar verim beyanına sahiptir . React.js'de verim bildirimi olsaydı şöyle olurdu:

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>
    );
  }
});

Istenilen çıktı:

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

Ne yazık ki, React.js'de bir yok <yield/>. Aynı çıktıyı elde etmek için Wrapper bileşenini nasıl tanımlarım?


Yanıtlar:



159

kullanma 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);

Bu, transclusionAçısal olarak da bilinir .

childrenReact'te özel bir pervane ve bileşeninizin etiketlerinin içinde ne olduğunu içerecektir (işte <App name={name}/>içeride Wrapper, bu yüzdenchildren

childrenBir bileşen için benzersiz olan mutlaka kullanmanız gerekmediğini ve isterseniz normal sahne özelliklerini de kullanabileceğinizi veya sahne malzemeleri ile çocukları karıştırabileceğinizi unutmayın:

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);

Bu, birçok kullanım için basit ve iyidir ve çoğu tüketici uygulaması için bunu tavsiye ederim.


sahne yapmak

Oluşturma işlevlerini bir bileşene geçirmek mümkündür, bu desen genellikle çağrılır render propve childrenpervane genellikle bu geri aramayı sağlamak için kullanılır.

Bu örüntü gerçekten mizanpaj için tasarlanmamıştır. Sargı bileşeni genellikle bir durumu tutmak ve yönetmek ve bunu oluşturma işlevlerine enjekte etmek için kullanılır.

Karşı örnek:

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

Daha da süslü hale gelebilir ve hatta bir nesne bile sağlayabilirsiniz

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

Mutlaka kullanmanız gerekmediğini unutmayın children, bu bir tat / API meselesidir.

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

Bugün itibariyle birçok kütüphane render sahne (React bağlamı, React-motion, Apollo ...) kullanmaktadır, çünkü insanlar bu API'yi HOC'lardan daha kolay bulma eğilimindedir. tepki-powerplug basit render-prop bileşenleri bir koleksiyon. tepki-benimseme kompozisyon yapmanıza yardımcı olur.


Yüksek Dereceli Bileşenler (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);

Bir Üst Düzey Bileşeni / HOC genellikle bir bileşen alır ve yeni bir bileşen döndüren bir fonksiyondur.

Daha Yüksek Dereceli Bir Bileşen kullanmak childrenveya kullanmaktan daha yüksek performans gösterebilir render props, çünkü sargı işlemeyi bir adım öteye kısa devre yapma yeteneğine sahip olabilir shouldComponentUpdate.

Burada kullanıyoruz PureComponent. Uygulamayı yeniden oluştururken, WrappedAppad pervane zaman içinde değişmezse, sarıcı "sahne (aslında ad) eskisi gibi olduğu için işlemem gerekmez" diyebilir. İle childrenyukarıda tabanlı bir çözüm, sarıcı olsa bile PureComponent, çocukların eleman olasılıkla her zaman sarılmış bileşen saf olsa bile, yeniden verecek sarmalayıcı anlamına ebeveyn kılan her şey, yeniden çünkü böyle değildir. Bunu hafifletmeye ve zaman içinde sabit bir eleman sağlamaya yardımcı olabilecek bir babel eklentisi var children.


Sonuç

Yüksek Sipariş Bileşenleri size daha iyi performans verebilir. Çok karmaşık değil ama ilk başta kesinlikle düşmanca görünüyor.

Bunu okuduktan sonra tüm kod tabanınızı HOC'a geçirmeyin. Uygulamanızın kritik yollarında, performans nedenleriyle çalışma zamanı sarmalayıcıları yerine HOC'ları kullanmak isteyebileceğinizi unutmayın, özellikle de aynı sarmalayıcı birçok kez kullanılıyorsa, onu bir HOC yapmayı düşünmeye değer.

Redux ilk önce bir çalışma zamanı sarıcısı kullandı <Connect>ve daha sonra connect(options)(Comp)performans nedenleriyle bir HOC'ye geçti (varsayılan olarak sarıcı saftır ve kullanılır shouldComponentUpdate). Bu, bu cevapta vurgulamak istediğim şeyin mükemmel bir örneğidir.

Bir bileşenin bir render-prop API'sı varsa, bunun üzerine bir HOC oluşturmak genellikle kolaydır, bu nedenle bir lib yazarsanız, önce bir render prop API'sı yazmalı ve sonunda bir HOC sürümü sunmalısınız. Apollo'nun <Query>render-prop bileşeni ve graphqlHOC bunu kullanarak yaptığı şey budur.

Şahsen, her ikisini de kullanıyorum, ancak şüpheye düştüğümde HOC'ları tercih ederim çünkü:

  • compose(hoc1,hoc2)(Comp)Sahne oluşturmakla karşılaştırıldığında onları ( ) oluşturmak daha deyimsel
  • Bana daha iyi performanslar verebilir
  • Bu tarz bir programa aşinayım

En sevdiğim araçların HOC sürümlerini kullanmaktan / oluşturmaktan çekinmeyin:

  • En Tepki Context.Consumercomp
  • yersiz en Subscribe
  • render prop graphqlyerine Apollo'nun HOC'sini kullanmaQuery

Bence, bazen sahne kodu daha okunabilir, bazen daha az yapmak ... Ben sahip olduğum kısıtlamalara göre en pragmatik çözümü kullanmaya çalışıyorum. Bazen okunabilirlik performanslardan daha önemlidir, bazen değil. Akıllıca seçin ve 2018'in her şeyi render-props'lara dönüştürme eğilimini takip etmeyin.


1
Bu yaklaşım aynı zamanda sahne alt bileşenini (bu durumda Merhaba) yapmak için kolaylaştırır. React 0.14. * 'Den itibaren, aksesuarları çocuk bileşenlerine geçirmenin tek yolu pahalı olabilecek React.createClone kullanmak olacaktır.
Mukesh Soni

2
Soru: Cevap "daha iyi performans" dan bahsediyor - anlamadım: diğer çözümle karşılaştırıldığında daha mı iyi?
Philipp

1
HOC'ler, daha önce oluşturmayı kısa devre yapabildikleri için çalışma zamanı sarmalayıcılarına göre daha iyi performans gösterebilir.
Sebastien Lorber

1
Teşekkür ederim! Sanki benim ayımdan kelimeler aldın ama onları daha fazla yetenekle ifade ediyorsun 👍
MacKentoch

1
Bu çok daha iyi bir cevap:] Teşekkürler!
cullanrocks

31

Sophie'nin cevabına ek olarak, çocuk bileşeni türlerini göndermede de böyle bir şey yaparak bir kullanım buldum:

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
Hakkında herhangi bir belge görmedim delegate, nasıl buldunuz?
NVI

4
bir bileşene istediğiniz desteği ekleyebilir ve 4. sıradaki this.props.delegate dosyasını kullanarak istediğiniz gibi adlandırabilirsiniz, ancak başka bir şey de adlandırabilirsiniz.
krs
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.