ReactJS Design Patterns - Class and Custom Component

I'm trying to get to know better best practices and design patterns using React, and I have a question which of two similar solutions is correct:

The first solution uses a class that does not extend the component and the constructor returns a set of elements based on the object, it looks a little prettier and less general in code.

class PostEntryElement {
	constructor(postObjectArray) {
		return (
      postObjectArray.map((postObject, index) => {
        return (
          <li style={{marginTop: '20px'}}>
            Author: {postObject.author}, 
            Body: {postObject.body},
            Created: {postObject.created},
            Title: {postObject.title}
          </li>
        )
       })
     )
	}
}

class MyComponent extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			exampleAPIBlogPostsResponse: [
				{
					title  : 'Title 1',
					body   : 'Body 1',
					author : 'Author 1',
					created: 'Created 1'
				}, {
					title  : 'Title 2',
					body   : 'Body 2',
					author : 'Author 2',
					created: 'Created 2'
				}, {
					title  : 'Title 3',
					body   : 'Body 3',
					author : 'Author 3',
					created: 'Created 3'
				}
			]
		};
	}

	render() {
		return (
			<div>
      	See All Posts Below:
				<div>
        { 
          this.state.exampleAPIBlogPostsResponse && 
          new PostEntryElement(this.state.exampleAPIBlogPostsResponse)
        }
	      </div>
			</div>
		);
	}
}

React.render(<MyComponent />, document.getElementById('container'));
      

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="container">
    <!-- This element contents will be replaced with your component. -->
</div>
      

Run codeHide result


The second solution uses a custom component with properties and dynamically passes them from objects, but has more code and looks less "clean".

class PostEntryElement extends React.Component {
	constructor(props) {
		super(props);
	}

	render() {
		return (
          <li style={{marginTop: '20px'}}>
            Author: {this.props.author}, 
            Body: {this.props.body},
            Created: {this.props.created},
            Title: {this.props.title}
          </li>
		);
	}
}

class MyComponent extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			exampleAPIBlogPostsResponse: [
				{
					title  : 'Title 1',
					body   : 'Body 1',
					author : 'Author 1',
					created: 'Created 1'
				}, {
					title  : 'Title 2',
					body   : 'Body 2',
					author : 'Author 2',
					created: 'Created 2'
				}, {
					title  : 'Title 3',
					body   : 'Body 3',
					author : 'Author 3',
					created: 'Created 3'
				}
			]
		};
	}

	generatePosts() {
		return (this.state.exampleAPIBlogPostsResponse.map((postObject, index) => {
			return (
       <PostEntryElement
				title={postObject.title}
				body={postObject.body}
				author={postObject.author}
				created={postObject.created} 
       />
      )
		 })
    );
	}

	render() {
		return (
			<div>
        See All Posts Below:
				<div>
					{this.state.exampleAPIBlogPostsResponse && this.generatePosts()}
				</div>
			</div>
		);
	}
}

React.render(<MyComponent />, document.getElementById('container'));
      

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="container">
    <!-- This element contents will be replaced with your component. -->
</div>
      

Run codeHide result


+3


source to share


3 answers


In React, your state should be managed by container components. Containers should usually be classes that extend Component

. Presentation components must be pure stateless functions that execute according to the properties provided by their container.



If you're curious, Thinking in React is a great introduction to React and how components should interact with each other.

+1


source


I approach this as if you were just another developer on my team and I have to work with your code. It's basically the same as returning to your code after a year and having to work with it again.

In short, solution # 2 is better, albeit not perfect. That's why:

1) Debugging. If you are not using React devtools you should try them. They will give you a better understanding of how React works. But in short: React expects to have complete control over all components. It wants to be able to instantiate your classes internally (via new

). Using it new

yourself to create any part of the view is usually bad, making it difficult to find problems.

2) Related C # 1. Prefer components over "floating markup". In React you can throw JSX anywhere, you should always try to make sure the markup belongs to a specific component. In the first example, when you create a new class type that is not a component, I now have to find out how you expect me to use it before I can. More work for me, more surface for mistakes. This is why there are standards. Use components.

3) In the first example, a non-component returns a list of items. But in React,

Components must return one root element

(see https://facebook.github.io/react/docs/components-and-props.html ). This is why @aeid's solution doesn't work as is. And I don't think he even noticed. I don't blame him. He was discarded by this bad practice. This is what React expects, so this is what React developers expect. Note that this problem would never have occurred if you had used the real component in the first place and forced yourself to conform to the standards.

4) Prefer to modernize your tools to downgrade your code. I think you are hoping solution # 1 is better because you don't have to publish "author", "body", "created" and "title" twice. Guess what? Including ES2017 in Babel gives you an object spread "operator". So, you can do this:

<PostEntryElement {...postObject} />

      



and not list all the details. While such authority can certainly be violated, I still recommend it if possible for you.

Anyway, for comparison. Now fix example # 2.

1) Go back to "no floating markup". In particular, if you find yourself writing markup outside of a component method render()

, this is a good sign to fetch a new component. In this case, the method MyComponent.generatePosts()

must be completely replaced with a new component, eg. PostEntryList

... This is perhaps a little more formulaic, but much easier to read.

2) Don't forget to use key

! As a general rule of thumb, when you use Array.prototoype.map()

in a method render()

(or wherever you write your markup if you insist on writing it outside of methods render()

), you need to add a unique property key

to each element.

3) As already mentioned, consider using pure functions for presentation components. In fact, I'm not really special. I find that removing the constructor is clear enough.

So all together:

class PostEntryList extends React.Component {
  render() {
		return (
      <ul>
        {this.props.elements.map((postObject, index) => (
          <li style={{marginTop: '20px'}} key={postObject.title}>
            Author: {postObject.author}, 
            Body: {postObject.body},
            Created: {this.props.created},
            Title: {this.props.title}
          </li>
        ))}
      </ul>
    );
  }
}

class MyComponent extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			exampleAPIBlogPostsResponse: [
				{
					title  : 'Title 1',
					body   : 'Body 1',
					author : 'Author 1',
					created: 'Created 1'
				}, {
					title  : 'Title 2',
					body   : 'Body 2',
					author : 'Author 2',
					created: 'Created 2'
				}, {
					title  : 'Title 3',
					body   : 'Body 3',
					author : 'Author 3',
					created: 'Created 3'
				}
			]
		};
	}

	render() {
		return (
			<div>
        See All Posts Below:
				<div>
					{this.state.exampleAPIBlogPostsResponse && <PostEntryList elements={this.state.exampleAPIBlogPostsResponse} />}
				</div>
			</div>
		);
	}
}

ReactDOM.render(<MyComponent />, document.getElementById('container'));
      

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="container">
    <!-- This element contents will be replaced with your component. -->
</div>
      

Run codeHide result


+1


source


if your component doesn't need to maintain any state, use stateless component

, they are pure functions that take props and display ui

PostEntryElement

component as a stateless component,

const PostEntryElement = (props) => {
        return (
      postObjectArray.map((postObject, index) => {
        return (
          <li style={{marginTop: '20px'}}>
            Author: {postObject.author}, 
            Body: {postObject.body},
            Created: {postObject.created},
            Title: {postObject.title}
          </li>
        )
       })
     )
}

      

if for some reason your component needs state then use stateful component

read this

0


source







All Articles