Composition vs Inheritance
# 10. Composition vs Inheritance
React has a powerful composition model, and we recommend using composition instead of inheritance to reuse code between components.
# Containment (Component Composition)
Some components don't know their children ahead of time. This is especially common for components like Sidebar or Dialog that represent generic "boxes."
# The children Prop (Similar to Vue Slots)
We suggest that these components use the special children prop to pass children elements through to their rendered output:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
// children is a special prop, not explicitly declared in the parent
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
// Content between component tags is passed as props.children
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Note
Similar to slots in Vue
# Passing Components via Props (Similar to Vue Named Slots)
In rare cases, you might need multiple "holes" in a component. In such cases, you can use your own convention instead of children: pass the needed content as props.
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
// Pass components via props
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
React elements like <Contacts /> and <Chat /> are just objects, so you can pass them as props like any other data. You can pass anything as props, including any data format.
Note
Similar to named slots in Vue
# Specialization (String and Component Composition)
Sometimes we think about components as being "special cases" of other components. For example, we might say that a WelcomeDialog is a special case of Dialog.
In React, this is also achieved by composition. A more "specific" component renders a more "generic" one and configures it with props:
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
// Here props.title is not a component object, but a string
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!"
/>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Class-Based Component Composition
Composition works equally well for components defined as classes.
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# So What About Inheritance? (Not Recommended)
At Facebook, we use React in thousands of components, and we haven't found any use cases where we would recommend creating component inheritance hierarchies.
Props and composition give you all the flexibility you need to customize a component's look and behavior in an explicit and safe way. Remember that components may accept arbitrary props, including primitive values, React elements, or functions.
If you want to reuse non-UI functionality between components, we suggest extracting it into a separate JavaScript module such as a function, object, or class. Components can import it directly without extending it through inheritance.