Реагировать.js: обертывание одного компонента в другой


многие языки шаблонов имеют операторы" slots "или" yield", которые позволяют делать какую-то инверсию управления, чтобы обернуть один шаблон внутри другого.

Угловое и опции"включение".

рельсов оператор yield. Если Реагировать.у js было заявление yield, оно выглядело бы так:

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>

Увы, Реагируют.js не имеет <yield/>. Как определить компонент оболочки для достичь того же результата?

3 142

3 ответа:

попробуй:

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

посмотреть Несколько Компонентов: Детей и тип реквизита детей в документах для получения дополнительной информации.

используя 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 является специальной опорой в React и будет содержать то, что находится внутри тегов вашего компонента (здесь <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 опора часто используется для обеспечения этого обратного вызова.

этот шаблон на самом деле не предназначен для макета. Компонент-оболочка обычно используется для хранения и управления некоторым состоянием и введения его в его рендеринг функции.

встречный пример:

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

An компонент высшего порядка / HOC обычно это функция, которая принимает компонент и возвращает новый компонент.

использование компонента более высокого порядка может быть более эффективным, чем использование children или render props, потому что обертка может иметь возможность короткого замыкания рендеринга на один шаг вперед с shouldComponentUpdate.

здесь мы используем PureComponent. При повторной визуализации приложения, если WrappedApp имя prop не меняется с течением времени, оболочка имеет возможность сказать: "мне не нужно рендерить, потому что реквизит (на самом деле, имя) такие же, как и раньше". С помощью children решение на основе выше, даже если обертка PureComponent, это не так, потому что дочерний элемент воссоздается каждый раз, когда родительский визуализирует, что означает, что оболочка, вероятно, всегда будет повторно визуализироваться, даже если обернутый компонент это чисто. Там есть babel plugin это может помочь смягчить это и обеспечить постоянную children элемент с течением времени.


вывод

компоненты более высокого порядка могут дать вам лучшую производительность. Это не так сложно, но это, конечно, выглядит недружелюбно на первый взгляд.

не переносите всю свою кодовую базу в HOC после прочтения этого. Просто помните, что на критических путях вашего приложения вы можете использовать HOCs вместо оболочек времени выполнения по соображениям производительности, особенно если одна и та же оболочка используется много раз, стоит подумать о том, чтобы сделать ее HOC.

Redux сначала использовал оболочку времени выполнения <Connect> и позже переключился на HOC connect(options)(Comp) по соображениям производительности (по умолчанию оболочка является чистой и использовать shouldComponentUpdate). Это прекрасная иллюстрация того, что я хотел подчеркнуть в этом ответе.

Примечание если компонент имеет API render-prop, как правило, легко создать HOC поверх него, поэтому, если вы являетесь автором lib, вы должны сначала написать API render prop и в конечном итоге предложить специальную версию. Вот что делает Аполлон с <Query> render-prop компонент, и graphql HOC с его помощью.

лично я использую оба, но когда сомневаюсь, я предпочитаю HOCs, потому что:

  • это более идиоматично, чтобы составить их (compose(hoc1,hoc2)(Comp)) по сравнению с рендер реквизит
  • это может дать мне лучшие выступления
  • я знаком с этим стилем Программирование

я не стесняюсь использовать / создавать специальные версии моих любимых инструментов:

    реагировать!--30-- > comp неустановленной это!--31-->
  • используя graphql HOC Аполлона вместо Query render prop

на мой взгляд, иногда рендер реквизит делает код более читаемым, иногда меньше... Я стараюсь использовать наиболее прагматичное решение в соответствии с ограничениями, которые у меня есть. Иногда читаемость есть важнее спектаклей, иногда нет. Выбирайте мудро и не следуйте за тенденцией 2018 года преобразования всего в render-props.

в дополнение к ответу Софи, я также нашел применение в отправке типов дочерних компонентов, делая что-то вроде этого:

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