223

Many template languages have "slots" or "yield" statements, that allow to do some sort of inversion of control to wrap one template inside of another.

Angular has "transclude" option.

Ruby/Rails has yield statement. If React.js had yield statement, it would look like this:

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

Desired output:

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

Alas, React.js doesn’t have a <yield/>. How do I define Wrapper component to achieve the same output?

Rimian
  • 36,864
  • 16
  • 117
  • 117
NVI
  • 14,907
  • 16
  • 65
  • 104
  • Does this answer your question? [How to pass in a react component into another react component to transclude the first component's content?](https://stackoverflow.com/questions/25797048/how-to-pass-in-a-react-component-into-another-react-component-to-transclude-the) – Michael Freidgeim May 01 '20 at 13:53

3 Answers3

271

Try:

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

See Multiple Components: Children and Type of the Children props in the docs for more info.

Sophie Alpert
  • 139,698
  • 36
  • 220
  • 238
203

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

This is also known as transclusion in Angular.

children is a special prop in React and will contain what is inside your component's tags (here <App name={name}/> is inside Wrapper, so it is the children

Note that you don't necessarily need to use children, which is unique for a component, and you can use normal props too if you want, or mix props and 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);

This is simple and fine for many usecases, and I'd recommend this for most consumer apps.


render props

It is possible to pass render functions to a component, this pattern is generally called render prop, and the children prop is often used to provide that callback.

This pattern is not really meant for layout. The wrapper component is generally used to hold and manage some state and inject it in its render functions.

Counter example:

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

You can get even more fancy and even provide an object

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

Note you don't necessarily need to use children, it is a matter of taste/API.

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

As of today, many libraries are using render props (React context, React-motion, Apollo...) because people tend to find this API more easy than HOC's. react-powerplug is a collection of simple render-prop components. react-adopt helps you do composition.


Higher-Order Components (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 Higher-Order Component / HOC is generally a function that takes a component and returns a new component.

Using an Higher-Order Component can be more performant than using children or render props, because the wrapper can have the ability to short-circuit the rendering one step ahead with shouldComponentUpdate.

Here we are using PureComponent. When re-rendering the app, if the WrappedApp name prop does not change over time, the wrapper has the ability to say "I don't need to render because props (actually, the name) are the same as before". With the children based solution above, even if the wrapper is PureComponent, it is not the case because the children element is recreated everytime the parent renders, which means the wrapper will likely always re-render, even if the wrapped component is pure. There is a babel plugin that can help mitigate this and ensure a constant children element over time.


Conclusion

Higher-Order Components can give you better performance. It's not so complicated but it certainly looks unfriendly at first.

Don't migrate your whole codebase to HOC after reading this. Just remember that on critical paths of your app you might want to use HOCs instead of runtime wrappers for performance reasons, particularly if the same wrapper is used a lot of times it's worth considering making it an HOC.

Redux used at first a runtime wrapper <Connect> and switched later to an HOC connect(options)(Comp) for performance reasons (by default, the wrapper is pure and use shouldComponentUpdate). This is the perfect illustration of what I wanted to highlight in this answer.

Note if a component has a render-prop API, it is generally easy to create a HOC on top of it, so if you are a lib author, you should write a render prop API first, and eventually offer an HOC version. This is what Apollo does with <Query> render-prop component, and the graphql HOC using it.

Personally, I use both, but when in doubt I prefer HOCs because:

  • It's more idiomatic to compose them (compose(hoc1,hoc2)(Comp)) compared to render props
  • It can give me better performances
  • I'm familiar with this style of programming

I don't hesitate to use/create HOC versions of my favorite tools:

  • React's Context.Consumer comp
  • Unstated's Subscribe
  • using graphql HOC of Apollo instead of Query render prop

In my opinion, sometimes render props make the code more readable, sometimes less... I try to use the most pragmatic solution according to the constraints I have. Sometimes readability is more important than performances, sometimes not. Choose wisely and don't bindly follow the 2018 trend of converting everything to render-props.

Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419
  • 1
    This approach also makes it easy to pass the props down do children component (in this case Hello). From React 0.14.* onwards the only way to pass props to children components would be to use React.createClone, which might be expensive. – Mukesh Soni Sep 10 '15 at 18:47
  • 2
    Question: The answer mentions "better performance" - what I don't understand: better compared to which other solution? – Philipp Aug 08 '16 at 22:03
  • 1
    HOCs can have better performance compared to runtime wrappers, as they can short-circuit the rendering earlier. – Sebastien Lorber Aug 09 '16 at 08:49
  • When you use PureComponent, you don't need to return function any more. Just remove one wrap function: ```js const wrapHOC = (WrappedComponent) => { class Wrapper extends React.PureComponent { render() { return (
    header
    footer
    ); } return Wrapper; } ```
    – ColorWin Jan 03 '18 at 03:45
  • 1
    Thank you! It is like you took words from my month but you express them with greater talent – MacKentoch Sep 20 '18 at 19:07
  • 1
    This is a much better answer :] Thanks! – cullanrocks Sep 13 '19 at 16:32
  • Absolutely beautiful! The HOC-method was exactly what I was looking for, thank you so much! – Sam Sverko Aug 07 '20 at 14:15
  • Can I use HOC without wrapping the PureComponent in a function? Pass the WrappedComponent in props or something like that? – Yechiam Weiss Jan 06 '21 at 09:59
  • How can I make the `wrapHOC ` function in typescript with Generic types? – rMonteiro Feb 03 '22 at 19:36
31

In addition to Sophie's answer, I also have found a use in sending in child component types, doing something like this:

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} />
    }
});
Cam Jackson
  • 11,860
  • 8
  • 45
  • 78
krs
  • 4,096
  • 19
  • 22
  • 2
    I haven’t seen any documentation on `delegate`, how did you find it? – NVI Jan 03 '14 at 10:08
  • 4
    you can add whatever props you want to a component and name them as you want, im using the this.props.delegate at row 4 but could just as well named it something else. – krs Jan 03 '14 at 13:39