Reputation: 1646
I'm faced with a decision about how to compose my components and I'm wondering if the community has any ideas about which is the best practice.
1: Encapsulate components to hide details.
Owner renders:
<List ... />
List renders:
<Item ... />
Item renders:
<div>...</div>
In this version the components aren't very reusable because they know alot about the domain details. The Owner doesn't know anything about an Item in a list.
2: Nest components deeply near the top, stateful view-controllers
Owner renders:
<List>
<Item>
<div>...</div>
</Item>
</List>
In this version, the Owner knows all about the composition of it's children, but the components themselves aren't very useful, because they know nothing about the domain. They are however more reusable.
3: Encapsulate generic components in domain specific components
This is a hybrid of option 1 and option 2
Owner renders:
<DomainList ... />
DomainList renders:
<List ...>
<DomainItem .../>
</List>
DomainItem renders:
<Item ...>
...
</Item>
The Owner knows nothing about the generic List, DomainItem, or generic Item.
Which approach is better? Has anyone had success or failure with any of these approaches? What happened?
Upvotes: 4
Views: 505
Reputation: 22933
Components aren't less reusable just because they dictate most things about how they should be rendered. You could even argue that they are more reusable since the component using it doesn't need to know anything about how it renders.
It's quite easy to get into a paralysis by analysis state when you start to think about these things. My approach to creating React components is:
this.props.children
for wrapper components that doesn't know about what content goes into it. General stuff like a popup, tab-view, etc.I tend to see two different types of components. The general purpose stuff that is either so general that I could reuse them between projects or application specific but general enough that I'll use them a lot in different parts of the application for different purposes. Then there's the specific components that have very specific purposes, things like "render this todo list" or "render this form to edit the todo". These tend to use the general purpose components, but definitely not the other way around.
I usually know pretty well when I start to create the component which of these types it's going to be. And for the general purpose components, I tend to make them as flexible and open as possible. For the specific purpose ones, I make them more like black boxes, sort of like "Just give me the data and I'll take it from there". Because they have very descriptive names and knows all there is to know about how to render themselves, it's easy to understand what's going on when reading code that uses them.
In your example, I'd say that a List
component is a general purpose component, and I'd definitely want to make it flexible. I'd probably have a specific component for the list I'm building as well, something like a TodoList
that could look something like:
var TodoList = React.createClass({
render() {
return (
<List>
{this.props.todos.map(todo => <ListItem>{todo.text}</ListItem>)}
</List>
);
}
});
Another option would be something like this:
var ListItem = React.createClass({
render() {
return <li>{this.props.item.text}</li>;
}
});
var TodoList = React.createClass({
render() {
return <List items={this.state.items} listItemComponent={ListItem} />;
}
});
In this case, the List
component iterates over all items and uses the passed listItemComponent
component to render the items.
TL;DR
Start out by making very specific components that knows a lot, and refactor them to be more general when you see a use case for reuse.
Upvotes: 3