# Добавить новость

Что такое добавление новости?

1. Это форма, в которую мы вводим необходимые данные.
2. Это "лента новостей", которая отображает наши данные.

У данной задачи есть масса вариантов решения. Мы начнем с канонического варианта: в общем родителе (`<App/>`) будем хранить state с новостями. В компонент `<Add />` будем передавать функцию (так как в props мы можем передавать что угодно), которая будет иметь доступ к state с новостями и которая в свою очередь будет добавлять новость в этот state.

Так как state у `<App />` будет изменяться, все дети (в том числе и новостная лента `<News />`) будут перерисованы, а следовательно - мы увидим добавленную новость.

## Взаимодействие из ребенка с родителем

**Шаг 1**: создадим состояние с новостями в `<App />` (а следовательно, переделаем App из stateless в statefull):

```jsx
class App extends React.Component {
  state = {
    news: myNews, // в начальное состояние положили значение из переменной
  }

  render() {
    return (
      <React.Fragment>
        <Add />
        <h3>Новости</h3>
        {/* считали новости из this.state */}
        <News data={this.state.news} />
      </React.Fragment>
    )
  }
}
```

Что примечательно, мы изменили источник данных для `<News />`, но компонент работает как ни в чем не бывало. Удобно!

**Шаг 2:** передадим функцию-обработчик в Add

```jsx
class App extends React.Component {
  state = {
    news: myNews,
  }
  handleAddNews = () => {
    console.log('я вызвана из Add, но имею доступ к this.state у App!', this.state)
  }
  render() {
    return (
      <React.Fragment>
        <Add onAddNews={this.handleAddNews} />
        <h3>Новости</h3>
        <News data={this.state.news} />
      </React.Fragment>
    )
  }
}
```

**Шаг 3:** вызовем функцию из `<Add />`, не забудем про *PropTypes*.

```jsx
class Add extends React.Component {
  state = {
    name: '',
    text: '',
    agree: false,
  }
  onBtnClickHandler = (e) => {
    e.preventDefault()
    const { name, text } = this.state
    // alert(name + '\n' + text)
    // вызываем вместо alert
    this.props.onAddNews()
  }
  ...
  render() {
    const { name, text, agree } = this.state
    return (
      <form className='add'>
        ...
        <button
          className='add__btn'
          onClick={this.onBtnClickHandler}
          disabled={!this.validate()}>
          Показать alert
              </button>
      </form>
    )
  }
}

Add.propTypes = {
  onAddNews: PropTypes.func.isRequired, // func используется для проверки передачи function
}
```

Проверим:

![add news log inside app](/files/-LvLnTgYqAalY4KG-fEM)

**Шаг 4:** из `<Add />` будем передавать объект с новостью.

```jsx
class Add extends React.Component {
  ...
  onBtnClickHandler = (e) => {
    e.preventDefault()
    const { name, text } = this.state
    // передаем name и text
    // big text у нас отсутствует :(
    this.props.onAddNews({ name, text })
  }
  ...
}
```

**Шаг 5:** полученный объект будем записывать на первое место в массиве с новостями в `<App />`. Разумеется, массив будем обновлять через *setState*.

```jsx
class App extends React.Component {
  ...
  handleAddNews = (data) => {
    // сначала мы формируем массив, на основе
    // всего того, что уже было в новостях
    // и кладем это все в новый массив + 
    // новую новость кладем в начало массива
    const nextNews = [data, ...this.state.news]

    // затем обновляем новый массив новостей в this.state.news
    this.setState({ news: nextNews })
  }
  ...
}
```

Проверим?

![add news error](/files/-LvLnTg_004nM3aDwdXx)

Окей, перед нами отличный кейс (реальный рабочий случай).

*Во-первых*: мы были почти прилежными учениками и сделали propTypes, для `<Article />`. Из ошибки сразу понятно: мы не передаем значение *author* (потому что, мы передаем *name*).

*Во-вторых*: мы все же накосячили, и забыли добавить в propTypes для `<Article />` свойство id. Хорошо, что кода мало и ошибка сразу нашлась. Не забывайте про перечисление всех свойств в `propTypes` - это супер шпаргалка.

*В-третьих*: мы из `<Add />` не передаем `id` и `bigText`.

Тем не менее, нельзя не отметить, что мы добавили динамики в наше приложение! Новость добавляется, причем счетчик новостей работает (а мы его вообще не трогали). Кто уже празднует - молодец, но кто хочет пофиксить все ошибки и сдать работу на пять - милости прошу в заключительный пункт основного курса.

## Работа над ошибками

Я бы хотел, чтобы все это вы пофиксили сами. Но, чтобы не быть автором, который "оп-па, косяк, давайте-ка я отдам это вам на домашку", я все сделаю и опишу. Просто попробуйте. У вас получится. Практика решает. Обязательно всегда практикуйтесь для закрепления материала.

**Задача**:

* добавить в форму добавления textarea для bigText;
* передавать author;
* передавать id (можно сделать через timestamp (отметку времени в ms): `+new Date()`). Для обучающего примера будет достаточно;
* исправить propTypes в `<News />`

![news added](/files/-LvLnTgbvwsNQ4inUB3L)

**Решение**:

Полный код того, что находится в `<body />` (с комментариями о последних изменениях)

```jsx
<body>
  <div id="root"></div>
  <script type="text/babel">

    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 {
      // удалили старое состояние counter: 0 (старый ненужный код)
      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: '', // добавлен bigText
        agree: false,
      }
      onBtnClickHandler = (e) => {
        e.preventDefault()
        const { name, text, bigText } = this.state // вытащили так же и bigText
        this.props.onAddNews({
          id: +new Date(), // в id сохраняется количество миллисекунд прошедших с 1 января 1970 года в часовом поясе UTC 
          author: name, // name сохраняем в поле author
          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>
            {/* добавили bigText */}
            <textarea
              id='bigText'
              onChange={this.handleChange}
              className='add__text'
              placeholder='Текст новости подробно'
              value={bigText}
            ></textarea>
            <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(
      <App />,
      document.getElementById('root')
    );

  </script>

</body>
```

Готово!

[Исходный код](https://github.com/maxfarseer/react-course-ru-v2/tree/chp12-add-news-from-children) на данный момент.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://max-frontend.gitbook.io/react-course-ru-v2/rabota-s-formoi/dobavit-novost.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
