简体   繁体   中英

What's the best way to inject a React component into another React component? Or should I bother?

While working on my first large React application, I started feeling uneasy about the tight coupling of nested components, especially when writing unit tests.

If is a high-level component, and its render function returns <div><Bar><Baz /></Bar></div> , I would like to be able to test Foo, Bar, and Baz in isolation.

I searched for advice but didn't find any. So, on my own, I came up with two methods of injection: factory functions and via the outer component's properties. (I am not talking about Children. I'm talking about "baked in" dependencies--the sort people usually import via require or import statements.

Properties

const Baz = React.createClass({
  render() {
    return <p>Inner</p>;
  }
});

const Foo = React.createClass({
  render() {
    const Bar = this.props.innerComponent;
    return <Bar />;
  }
});

ReactDOM.render(
  <Foo innerComponent={Baz} />,
  document.getElementById('container')
);

Factory

const Baz = React.createClass({
 render() {
   return <p>Inner</p>;
 }
});

const fooFactory = innerComponent => {
  return  React.createClass({
    render() {
      const Bar = innerComponent;
      return <Bar />;
   }
 });
};

const Foo = fooFactory(Baz);

ReactDOM.render(
  <Foo innerComponent={Baz} />,
  document.getElementById('container')
);

Or am I just taking decoupling too far? I see almost no one else doing this in any tutorials or examples.

Would you be inclined to inject components sometimes but not other times? In what circumstances? And, if you did it, would you use one of the above techniques or do it some other way?

This is a pattern you'll see from time to time when there is a run-time necessity for specifying a component type which the component it's passed to will instantiate itself; for example, the React TransitionGroup component takes such a property called component :

By default ReactTransitionGroup renders as a span . You can change this behavior by providing a component prop. For example, here's how you would render a <ul> :

 <ReactTransitionGroup component="ul"> ... </ReactTransitionGroup>

Every DOM component that React can render is available for use. However, component does not need to be a DOM component. It can be any React component you want; even ones you've written yourself! Just write component={List} and your component will receive this.props.children .

It's less common to use this pattern for the user-specified contents of a component, since this is solved more elegantly by this.props.children .

However, as far as testing is concerned, the common solution is simply to export each component separately and test it in isolation. If a high-level component composes them in a certain way, all you really need to test is that they are composed properly, since the components are also tested individually.

There is a common pattern of separating "smart" and "dumb" components; dumb components simply render what they're given, and are very easily unit tested. Smart components may fetch data (eg via Ajax, or from a flux store), compose specific dumb components, or make other "decisions," and can be tougher to test—but again, since the dumb components are already tested, ideally you can just check that the smart component behaves correctly and renders the right thing.

To use your example, it's Foo 's job to render <div><Bar><Baz /></Bar></div> ; it's a smart component, and the test should ensure that it renders exactly that. Bar 's job, however, is to render its children, and is more easily tested.

You also have the ability to mock out a component with TestUtils.mockComponent .

Check out mochajs . This testing framework overrides import / require , which solves your problem.

It does mean that your subcomponents need to be written as ECMA 6 or NodeJS modules, though - but that really just means they need to be in separate source files.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM