Порефакторим...
Для начала, удалите вовсе компонент <Comments />const Comments... соответственно).
Далее, давайте представим: у наших новостей появляются какие-то дополнительные поля, пользователь начинает взаимодействовать с ними, например "пометить как прочитанное" и так далее. Нам было бы удобно, чтобы каждая новость была представлена отдельным компонентом.
Задача: <News /> должен рендерить список компонентов <Article />. Каждый компонент <Article /> должен получать соответствующие данные, например: первый экземпляр получит первый элемент массива, второй - второй и так далее.
То есть, раньше в map мы возвращали JSX-разметку. Но мы так же можем возвращать и компонент.
Попробуйте сами, а потом смотрите решение ниже.
Подсказка #1: if-else нашего компонента <News />
1
if (data.length) {
2
newsTemplate = data.map(function(item) {
3
return <Article key={item.id} data={item}/>
4
})
5
} else {
6
newsTemplate = <p>К сожалению новостей нет</p>
7
}
Copied!
Подсказка #2 (по сути решение задачи): компонент <Article />
1
class Article extends React.Component {
2
render() {
3
const { author, text } = this.props.data
4
return (
5
<div className="article">
6
<p className="news__author">{author}:</p>
7
<p className="news__text">{text}</p>
8
</div>
9
)
10
}
11
}
Copied!
Что любопытно, больше не изменилось ни-че-го.
Добавьте заголовок "Новости" в <App /> перед компонентом <News />
1
const App = () => {
2
return (
3
<React.Fragment>
4
<h3>Новости</h3>
5
<News data={myNews}/>
6
</React.Fragment>
7
)
8
}
Copied!
Добавьте красоты (CSS) по вкусу, либо возьмите мой вариант:
1
.none {
2
display: none;
3
}
4
5
body {
6
background: rgba(0, 102, 255, 0.38);
7
font-family: sans-serif;
8
}
9
10
p {
11
margin: 0 0 5px;
12
}
13
14
.article {
15
background: #FFF;
16
border: 1px solid rgba(0, 89, 181, 0.82);
17
width: 600px;
18
margin: 0 0 5px;
19
box-shadow: 2px 2px 5px -1px rgb(0, 81, 202);
20
padding: 3px 5px;
21
}
22
23
.news__author {
24
text-decoration: underline;
25
color: #007DDC;
26
}
27
.news__count {
28
margin: 10px 0 0 0;
29
display: block;
30
}
Copied!
С новыми стилями, код сценария выглядит так:
index.html
1
<!DOCTYPE html>
2
<html>
3
<head>
4
<meta charset="UTF-8" />
5
<title>React [RU] Tutorial v2</title>
6
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
7
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
8
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
9
10
<style>
11
.none {
12
display: none;
13
}
14
15
body {
16
background: rgba(0, 102, 255, 0.38);
17
font-family: sans-serif;
18
}
19
20
p {
21
margin: 0 0 5px;
22
}
23
24
.article {
25
background: #FFF;
26
border: 1px solid rgba(0, 89, 181, 0.82);
27
width: 600px;
28
margin: 0 0 5px;
29
box-shadow: 2px 2px 5px -1px rgb(0, 81, 202);
30
padding: 3px 5px;
31
}
32
33
.news__author {
34
text-decoration: underline;
35
color: #007DDC;
36
}
37
.news__count {
38
margin: 10px 0 0 0;
39
display: block;
40
}
41
</style>
42
</head>
43
<body>
44
<div id="root"></div>
45
<script type="text/babel">
46
47
const myNews = [
48
{
49
id: 1, // добавили id
50
author: 'Саша Печкин',
51
text: 'В четверг, четвертого числа...'
52
},
53
{
54
id: 2,
55
author: 'Просто Вася',
56
text: 'Считаю, что $ должен стоить 35 рублей!'
57
},
58
{
59
id: 3,
60
author: 'Max Frontend',
61
text: 'Прошло 2 года с прошлых учебников, а $ так и не стоит 35'
62
},
63
{
64
id: 4,
65
author: 'Гость',
66
text: 'Бесплатно. Без смс, про реакт, заходи - https://maxpfrontend.ru'
67
}
68
];
69
70
class Article extends React.Component {
71
render() {
72
const { author, text } = this.props.data
73
return (
74
<div className="article">
75
<p className="news__author">{author}:</p>
76
<p className="news__text">{text}</p>
77
</div>
78
)
79
}
80
}
81
82
class News extends React.Component {
83
render() {
84
const { data } = this.props
85
let newsTemplate
86
87
if (data.length) {
88
newsTemplate = data.map(function(item) {
89
return <Article key={item.id} data={item}/>
90
})
91
} else {
92
newsTemplate = <p>К сожалению новостей нет</p>
93
}
94
95
return (
96
<div className="news">
97
{newsTemplate}
98
{
99
data.length ? <strong className={'news__count'}>Всего новостей: {data.length}</strong> : null
100
}
101
</div>
102
);
103
}
104
}
105
106
const App = () => {
107
return (
108
<React.Fragment>
109
<h3>Новости</h3>
110
<News data={myNews}/>
111
</React.Fragment>
112
)
113
}
114
115
ReactDOM.render(
116
<App />,
117
document.getElementById('root')
118
);
119
120
</script>
121
122
</body>
123
</html>
Copied!
В целом меня устраивает почти все. Осталось немного отполировать метод render компонента <News />. Правило следующее: стараемся в render держать как можно меньше кода, чтобы его было легко читать вашим коллегам. Для этого, мы newsTemplate будем заполнять внутри нового метода, который будем вызывать в render.
1
class News extends React.Component {
2
renderNews = () => {
3
const { data } = this.props
4
let newsTemplate = null
5
6
if (data.length) {
7
newsTemplate = data.map(function(item) {
8
return <Article key={item.id} data={item}/>
9
})
10
} else {
11
newsTemplate = <p>К сожалению новостей нет</p>
12
}
13
14
return newsTemplate
15
}
16
render() {
17
const { data } = this.props
18
19
return (
20
<div className="news">
21
{this.renderNews()}
22
{
23
data.length ? <strong className={'news__count'}>Всего новостей: {data.length}</strong> : null
24
}
25
</div>
26
);
27
}
28
}
Copied!
Что примечательного в этом коде?
Мы создали метод renderNews (внутри class) с помощью так называемой "жирной стрелочной функции" (запись вида methodName = () => ...). При такой записи, внутри функции мы не теряем контекст this. То есть, можем обращаться к this.props, например.
Почему мы метод render не описываем через жирную стрелочную функцию? Потому что, это метод жизненного цикла react-компонента, и туда this "прокидывает" уже сам react.
Далее, так как мы renderNews создали в качестве метода, значит внутри компонента, мы должны обращаться к нему как this.XXX (где XXX - название метода, в нашем случае renderNews).
Что же изменилось, спросите вы? Было много кода в render, стало чуть выше. Дело в том, что когда ваши компоненты разрастутся, очень удобно иметь "рендер" хорошо читаемым, то есть таким, в котором все лишнее спрятано.
Посмотрим, что вышло в итоге:
Исходный код на данный момент.
Last modified 1yr ago
Copy link