Порефакторим...

Для начала, удалите вовсе компонент <Comments />const Comments... соответственно).

Далее, давайте представим: у наших новостей появляются какие-то дополнительные поля, пользователь начинает взаимодействовать с ними, например "пометить как прочитанное" и так далее. Нам было бы удобно, чтобы каждая новость была представлена отдельным компонентом.

Задача: <News /> должен рендерить список компонентов <Article />. Каждый компонент <Article /> должен получать соответствующие данные, например: первый экземпляр получит первый элемент массива, второй - второй и так далее.

То есть, раньше в map мы возвращали JSX-разметку. Но мы так же можем возвращать и компонент.

Попробуйте сами, а потом смотрите решение ниже.

Подсказка #1: if-else нашего компонента <News />

if (data.length) {
newsTemplate = data.map(function(item) {
return <Article key={item.id} data={item}/>
})
} else {
newsTemplate = <p>К сожалению новостей нет</p>
}

Подсказка #2 (по сути решение задачи): компонент <Article />

class Article extends React.Component {
render() {
const { author, text } = this.props.data
return (
<div className="article">
<p className="news__author">{author}:</p>
<p className="news__text">{text}</p>
</div>
)
}
}

Что любопытно, больше не изменилось ни-че-го.

Добавьте заголовок "Новости" в <App /> перед компонентом <News />

const App = () => {
return (
<React.Fragment>
<h3>Новости</h3>
<News data={myNews}/>
</React.Fragment>
)
}

Добавьте красоты (CSS) по вкусу, либо возьмите мой вариант:

.none {
display: none;
}
body {
background: rgba(0, 102, 255, 0.38);
font-family: sans-serif;
}
p {
margin: 0 0 5px;
}
.article {
background: #FFF;
border: 1px solid rgba(0, 89, 181, 0.82);
width: 600px;
margin: 0 0 5px;
box-shadow: 2px 2px 5px -1px rgb(0, 81, 202);
padding: 3px 5px;
}
.news__author {
text-decoration: underline;
color: #007DDC;
}
.news__count {
margin: 10px 0 0 0;
display: block;
}

С новыми стилями, код сценария выглядит так:

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React [RU] Tutorial v2</title>
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
<style>
.none {
display: none;
}
body {
background: rgba(0, 102, 255, 0.38);
font-family: sans-serif;
}
p {
margin: 0 0 5px;
}
.article {
background: #FFF;
border: 1px solid rgba(0, 89, 181, 0.82);
width: 600px;
margin: 0 0 5px;
box-shadow: 2px 2px 5px -1px rgb(0, 81, 202);
padding: 3px 5px;
}
.news__author {
text-decoration: underline;
color: #007DDC;
}
.news__count {
margin: 10px 0 0 0;
display: block;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const myNews = [
{
id: 1, // добавили id
author: 'Саша Печкин',
text: 'В четверг, четвертого числа...'
},
{
id: 2,
author: 'Просто Вася',
text: 'Считаю, что $ должен стоить 35 рублей!'
},
{
id: 3,
author: 'Max Frontend',
text: 'Прошло 2 года с прошлых учебников, а $ так и не стоит 35'
},
{
id: 4,
author: 'Гость',
text: 'Бесплатно. Без смс, про реакт, заходи - https://maxpfrontend.ru'
}
];
class Article extends React.Component {
render() {
const { author, text } = this.props.data
return (
<div className="article">
<p className="news__author">{author}:</p>
<p className="news__text">{text}</p>
</div>
)
}
}
class News extends React.Component {
render() {
const { data } = this.props
let newsTemplate
if (data.length) {
newsTemplate = data.map(function(item) {
return <Article key={item.id} data={item}/>
})
} else {
newsTemplate = <p>К сожалению новостей нет</p>
}
return (
<div className="news">
{newsTemplate}
{
data.length ? <strong className={'news__count'}>Всего новостей: {data.length}</strong> : null
}
</div>
);
}
}
const App = () => {
return (
<React.Fragment>
<h3>Новости</h3>
<News data={myNews}/>
</React.Fragment>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
</script>
</body>
</html>

В целом меня устраивает почти все. Осталось немного отполировать метод render компонента <News />. Правило следующее: стараемся в render держать как можно меньше кода, чтобы его было легко читать вашим коллегам. Для этого, мы newsTemplate будем заполнять внутри нового метода, который будем вызывать в render.

class News extends React.Component {
renderNews = () => {
const { data } = this.props
let newsTemplate = null
if (data.length) {
newsTemplate = data.map(function(item) {
return <Article key={item.id} data={item}/>
})
} else {
newsTemplate = <p>К сожалению новостей нет</p>
}
return newsTemplate
}
render() {
const { data } = this.props
return (
<div className="news">
{this.renderNews()}
{
data.length ? <strong className={'news__count'}>Всего новостей: {data.length}</strong> : null
}
</div>
);
}
}

Что примечательного в этом коде?

Мы создали метод renderNews (внутри class) с помощью так называемой "жирной стрелочной функции" (запись вида methodName = () => ...). При такой записи, внутри функции мы не теряем контекст this. То есть, можем обращаться к this.props, например.

Почему мы метод render не описываем через жирную стрелочную функцию? Потому что, это метод жизненного цикла react-компонента, и туда this "прокидывает" уже сам react.

Далее, так как мы renderNews создали в качестве метода, значит внутри компонента, мы должны обращаться к нему как this.XXX (где XXX - название метода, в нашем случае renderNews).

Что же изменилось, спросите вы? Было много кода в render, стало чуть выше. Дело в том, что когда ваши компоненты разрастутся, очень удобно иметь "рендер" хорошо читаемым, то есть таким, в котором все лишнее спрятано.

Посмотрим, что вышло в итоге:

Исходный код на данный момент.