Порефакторим...
Для начала, удалите вовсе компонент <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/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/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, стало чуть выше. Дело в том, что когда ваши компоненты разрастутся, очень удобно иметь "рендер" хорошо читаемым, то есть таким, в котором все лишнее спрятано.
Посмотрим, что вышло в итоге:

Исходный код на данный момент.
Last updated
Was this helpful?