create-react-app

Здесь я буду предельно краток: facebook выкатили удобный инструмент для старта приложения. Поддерживается новый синтаксис, импорты, тестирование, перезагрузка страницы при изменениях, линтер и многое другое.

Во всем разнообразии мы сейчас разбираться не будем. Цель: разбить index.html на компоненты, подключить их, навести порядок.

Скопируйте index.html куда-нибудь на память, скоро мы разобьем его на мелкие удобные компоненты.

Установка и запуск create-react-app

npx create-react-app my-app
cd my-app
npm start

Если вы не знакомы с данными командами, значит вам нужно поставить себе node.js и ввести их в терминале после.

После запуска мы получим следующую картину в браузере:

welcome to react

И следующую файловую структуру:

+-- node_modules (здесь расположены пакеты для работы приложения)
+-- public (здесь расположены публичные файлы, такие как index.html и favicon)
+-- src (здесь сейчас уже живет компонент App)
+-- .gitignore (файл для гита)
+-- package.json (файл с зависимостями проекта)
+-- README.md (описание проекта)
+-- yarn.lock (может быть, а может и не быть - тоже относится к теме зависимостей проекта)

Восстановим баланс в src

src/App.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;
}
.test-input {
margin: 0 5px 5px 0;
}
.add {
margin: 0 5px 5px 0;
width: 210px;
border: 1px dashed rgba(0, 89, 181, 0.82);
padding: 5px;
}
.add__author, .add__text, .add__btn, .add__checkrule {
display: block;
margin: 0 0 5px 0;
padding: 5px;
width: 94%;
border: 1px solid rgba(0, 89, 181, 0.82);
}
.add__checkrule {
border: none;
font-size: 12px;
}
.add__btn {
box-sizing: content-box;
color: #FFF;
text-transform: uppercase;
background: #007DDC;
}
.add__btn:disabled {
background: #CCC;
color: #999;
}

src/App.js (копируем почти все из тэга script)

import React from 'react'; // подключение библиотеки React
import './App.css'; // подключение файла стилей
// далее скопировано из тэга script
const myNews = [
{
id: 1,
author: "Саша Печкин",
text: "В четверг, четвертого числа...",
bigText:
"в четыре с четвертью часа четыре чёрненьких чумазеньких чертёнка чертили чёрными чернилами чертёж."
},
{
id: 2,
author: "Просто Вася",
text: "Считаю, что $ должен стоить 35 рублей!",
bigText: "А евро 42!"
},
{
id: 3,
author: "Max Frontend",
text: "Прошло 2 года с прошлых учебников, а $ так и не стоит 35",
bigText: "А евро опять выше 70."
},
{
id: 4,
author: "Гость",
text: "Бесплатно. Без смс, про реакт, заходи - https://maxpfrontend.ru",
bigText:
"Еще есть группа VK, telegram и канал на youtube! Вся инфа на сайте, не реклама!"
}
];
class Article extends React.Component {
state = {
visible: false
};
handleReadMoreClck = e => {
e.preventDefault();
this.setState({ visible: true });
};
render() {
const { author, text, bigText } = this.props.data;
const { visible } = this.state;
return (
<div className="article">
<p className="news__author">{author}:</p>
<p className="news__text">{text}</p>
{!visible && (
<a
onClick={this.handleReadMoreClck}
href="#"
className="news__readmore"
>
Подробнее
</a>
)}
{visible && <p className="news__big-text">{bigText}</p>}
</div>
);
}
}
Article.propTypes = {
data: PropTypes.shape({
id: PropTypes.number.isRequired, // добавили id, это число, обязательно
author: PropTypes.string.isRequired,
text: PropTypes.string.isRequired,
bigText: PropTypes.string.isRequired
})
};
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>
);
}
}
News.propTypes = {
data: PropTypes.array.isRequired
};
class Add extends React.Component {
state = {
name: "",
text: "",
bigText: "",
agree: false
};
onBtnClickHandler = e => {
e.preventDefault();
const { name, text, bigText } = this.state;
this.props.onAddNews({
id: +new Date(),
author: name,
text,
bigText
});
};
handleChange = e => {
const { id, value } = e.currentTarget;
this.setState({ [id]: e.currentTarget.value });
};
handleCheckboxChange = e => {
this.setState({ agree: e.currentTarget.checked });
};
validate = () => {
const { name, text, agree } = this.state;
if (name.trim() && text.trim() && agree) {
return true;
}
return false;
};
render() {
const { name, text, bigText, agree } = this.state;
return (
<form className="add">
<input
id="name"
type="text"
onChange={this.handleChange}
className="add__author"
placeholder="Ваше имя"
value={name}
/>
<textarea
id="text"
onChange={this.handleChange}
className="add__text"
placeholder="Текст новости"
value={text}
/>
<textarea
id="bigText"
onChange={this.handleChange}
className="add__text"
placeholder="Текст новости подробно"
value={bigText}
/>
<label className="add__checkrule">
<input type="checkbox" onChange={this.handleCheckboxChange} /> Я
согласен с правилами
</label>
<button
className="add__btn"
onClick={this.onBtnClickHandler}
disabled={!this.validate()}
>
Показать alert
</button>
</form>
);
}
}
Add.propTypes = {
onAddNews: PropTypes.func.isRequired
};
class App extends React.Component {
state = {
news: myNews
};
handleAddNews = data => {
const nextNews = [data, ...this.state.news];
this.setState({ news: nextNews });
};
render() {
return (
<React.Fragment>
<Add onAddNews={this.handleAddNews} />
<h3>Новости</h3>
<News data={this.state.news} />
</React.Fragment>
);
}
}
// скопировано все кроме ReactDOM.render
// добавился export
export default App;

Удалите файл src/Logo.svg, остальное не трогайте, но можете посмотреть :)

Create-react-app (CRA) при каждом изменении в файлах внутри директории src - перезагружает страницу в браузере.

prop types problem

У нас нет PropTypes в проекте. Так как раньше это был тэг script, а теперь нам нужен npm-пакет.

Остановите работу create-react-app в терминале и добавьте пакет prop-types

npm install --save prop-types

Я не привожу сюда примеров для yarn, так как если у вас yarn вы итак в курсе, как ставить пакеты через него.

Подключите PropTypes в начале файла:

src/App.js

import React from 'react'
import PropTypes from 'prop-types'
import './App.css'
...

Запустите приложение еще раз:

npm start
news cra works

Итого: мы перевели приложение на CRA.

Исходный код на данный момент (добавлен конфиг для преттира - .prettierrc, не обращайте внимание).