• No results found

We have seen what are the best ways to create components and the scenarios where it makes sense to use a local state. We have also seen how we can make our components reusable defining a clear interface with prop types.

Let's now dive into a real world example and take a look at how we can transform a non- reusable component into a reusable one with a generic and cleaner interface.

Suppose we have a component that loads a collection of posts from an API endpoint, and it shows the list on the screen.

It is a simplified example, but it is useful for understanding the necessary steps we need to take to make components reusable.

The component is defined as follows:

class PostList extends React.Component

With the constructor and a life cycle method:

constructor(props) { super(props) this.state = { posts: [], } } componentDidMount() { Posts.fetch().then(posts => { this.setState({ posts }) }) }

An empty array gets assigned to posts to represent the initial state.

During componentDidMount, the API call gets fired, and as soon as the data is available, the posts are stored in the state.

This is a very common data fetching pattern, and we will see the other possible approaches in Chapter 5, Proper Data Fetching.

Posts is a helper class that we use to communicate with the API, and it has a fetch method which returns a Promise that gets resolved with a list of posts.

We can now move into the part where the posts are displayed:

render() { return ( <ul> {this.state.posts.map(post => ( <li key={post.id}> <h1>{post.title}</h1>

{post.excerpt && <p>{post.excerpt}</p>} </li>

))} </ul> ) }

Inside the render method, we loop through the posts, and we map each one of them into a <li> element.

We assume that the title field is always present, and we show it inside an <h1> while the excerpt is optional, and we show it inside a paragraph only if it exists.

The above component works fine, and it has no problems.

Now, suppose that we need to render a similar list but this time, we want to display a list of users received from the props rather than the state (to make clear that we can serve different scenarios):

const UserList = ({ users }) => ( <ul>

{users.map(user => ( <li key={user.id}>

<h1>{user.username}</h1>

{user.bio && <p>{user.bio}</p>} </li>

))} </ul> )

Given a collection of users, the code above renders an unordered list very similar to the posts one.

Duplicating the code is usually not the best solution so let's see how React can help us to keep our code Don't Repeat Yourself (DRY). The first step to creating a reusable List component is to abstract it a little and decouple it from the data it has to display and we do that by defining a generic collection property. The main requirement is that, for the posts, we want to display the title and the excerpt; while, for the users, we have to show the username and the bio.

For doing that, we create two props: one called titleKey where we specify the name of the attribute to be displayed and one called textKey that we use to specify the optional field. The props of the new reusable List are the following:

List.propTypes = {

collection: React.PropTypes.array, textKey: React.PropTypes.string, titleKey: React.PropTypes.string, }

Since the List is not going to have any state or function, we can write it as a stateless functional component:

const List = ({ collection, textKey, titleKey }) => ( <ul> {collection.map(item => <Item key={item.id} text={item[textKey]} title={item[titleKey]} /> )} </ul> )

The List receives the props, and iterates over the collection, mapping all the items into another component (that we are going to create next). As you can see, we are passing to the children titles and text props which represent the values of the main attribute and the optional one, respectively.

The Item component is very simple and clean:

const Item = ({ text, title }) => ( <li>

<h1>{title}</h1>

{text && <p>{text}</p>} </li>

Item.propTypes = {

text: React.PropTypes.string, title: React.PropTypes.string, }

So we've created two components with a well-defined surface area which can we use together to display posts, users or any other kinds of lists. Smaller components are better for several reasons: for example, they are more maintainable and testable which make it easier to find and fix bugs.

Great, we can now rewrite our two components, PostsList and UsersList, to make them use the generic reusable list and avoid duplicating code.

Let's modify the render method of PostsLists as follows:

render() { return ( <List collection={this.state.posts} textKey="excerpt" titleKey="title" /> ) }

And the UserList function as follows:

const UserList = ({ users }) => ( <List collection={users} textKey="bio" titleKey="username" /> )

We went from a single-purpose component to a reusable one using the props to create a generic and well-defined interface.

It is now possible to reuse this component as many times as we need in our application and every developer can easily understand how to implement it thanks to the prop types. We could also go a step further using react-docgen to document our reusable list, as we have seen in the previous section.

The benefits of using a reusable component over a component which is coupled with the data it handles are many.

Suppose, for example, that we want to add logic to hide and show the optional field only when a button is clicked.

Alternatively, perhaps there is a new requirement to add a check and, if the title attribute is longer than twenty five characters, it gets cut and hyphenate.

We can now make the change at one single point, and all the components that are using it will benefit from the modification.