Это форма, в которую мы вводим необходимые данные.
Это "лента новостей", которая отображает наши данные.
У данной задачи есть масса вариантов решения. Мы начнем с канонического варианта: в общем родителе (<App/>) будем хранить state с новостями. В компонент <Add /> будем передавать функцию (так как в props мы можем передавать что угодно), которая будет иметь доступ к state с новостями и которая в свою очередь будет добавлять новость в этот state.
Так как state у <App /> будет изменяться, все дети (в том числе и новостная лента <News />) будут перерисованы, а следовательно - мы увидим добавленную новость.
Взаимодействие из ребенка с родителем
Шаг 1: создадим состояние с новостями в <App /> (а следовательно, переделаем App из stateless в statefull):
classAppextendsReact.Component { state = { news: myNews,// в начальное состояние положили значение из переменной }render() {return ( <React.Fragment> <Add /> <h3>Новости</h3> {/* считали новости из this.state */} <Newsdata={this.state.news} /> </React.Fragment> ) }}
Что примечательно, мы изменили источник данных для <News />, но компонент работает как ни в чем не бывало. Удобно!
Шаг 2: передадим функцию-обработчик в Add
classAppextendsReact.Component { state = { news: myNews, }handleAddNews= () => {console.log('я вызвана из Add, но имею доступ к this.state у App!',this.state) }render() {return ( <React.Fragment> <AddonAddNews={this.handleAddNews} /> <h3>Новости</h3> <Newsdata={this.state.news} /> </React.Fragment> ) }}
Шаг 3: вызовем функцию из <Add />, не забудем про PropTypes.
classAddextendsReact.Component { state = { name:'', text:'', agree:false, }onBtnClickHandler= (e) => {e.preventDefault()const { name,text } =this.state// alert(name + '\n' + text)// вызываем вместо alertthis.props.onAddNews() }...render() {const { name,text,agree } =this.statereturn ( <formclassName='add'> ... <buttonclassName='add__btn'onClick={this.onBtnClickHandler}disabled={!this.validate()}> Показать alert </button> </form> ) }}Add.propTypes = { onAddNews:PropTypes.func.isRequired,// func используется для проверки передачи function}
Проверим:
Шаг 4: из <Add /> будем передавать объект с новостью.
classAddextendsReact.Component {...onBtnClickHandler= (e) => {e.preventDefault()const { name,text } =this.state// передаем name и text// big text у нас отсутствует :(this.props.onAddNews({ name, text }) }...}
Шаг 5: полученный объект будем записывать на первое место в массиве с новостями в <App />. Разумеется, массив будем обновлять через setState.
classAppextendsReact.Component {...handleAddNews= (data) => {// сначала мы формируем массив, на основе// всего того, что уже было в новостях// и кладем это все в новый массив + // новую новость кладем в начало массиваconstnextNews= [data,...this.state.news]// затем обновляем новый массив новостей в this.state.newsthis.setState({ news: nextNews }) }...}
Проверим?
Окей, перед нами отличный кейс (реальный рабочий случай).
Во-первых: мы были почти прилежными учениками и сделали propTypes, для <Article />. Из ошибки сразу понятно: мы не передаем значение author (потому что, мы передаем name).
Во-вторых: мы все же накосячили, и забыли добавить в propTypes для <Article /> свойство id. Хорошо, что кода мало и ошибка сразу нашлась. Не забывайте про перечисление всех свойств в propTypes - это супер шпаргалка.
В-третьих: мы из <Add /> не передаем id и bigText.
Тем не менее, нельзя не отметить, что мы добавили динамики в наше приложение! Новость добавляется, причем счетчик новостей работает (а мы его вообще не трогали). Кто уже празднует - молодец, но кто хочет пофиксить все ошибки и сдать работу на пять - милости прошу в заключительный пункт основного курса.
Работа над ошибками
Я бы хотел, чтобы все это вы пофиксили сами. Но, чтобы не быть автором, который "оп-па, косяк, давайте-ка я отдам это вам на домашку", я все сделаю и опишу. Просто попробуйте. У вас получится. Практика решает. Обязательно всегда практикуйтесь для закрепления материала.
Задача:
добавить в форму добавления textarea для bigText;
передавать author;
передавать id (можно сделать через timestamp (отметку времени в ms): +new Date()). Для обучающего примера будет достаточно;
исправить propTypes в <News />
Решение:
Полный код того, что находится в <body /> (с комментариями о последних изменениях)
<body> <divid="root"></div> <scripttype="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.statereturn ( <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.propslet newsTemplate =nullif (data.length) { newsTemplate =data.map(function(item) {return <Articlekey={item.id} data={item}/> }) } else { newsTemplate = <p>К сожалению новостей нет</p> }return newsTemplate }render() { const { data } = this.propsreturn ( <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 // вытащили так же и bigTextthis.props.onAddNews({ id:+newDate(),// в id сохраняется количество миллисекунд прошедших с 1 января 1970 года в часовом поясе UTC author: name,// name сохраняем в поле author text, bigText, }) }handleChange= (e) => {const { id,value } =e.currentTargetthis.setState({ [id]:e.currentTarget.value }) }handleCheckboxChange= (e) => {this.setState({ agree:e.currentTarget.checked }) }validate= () => {const { name,text,agree } =this.stateif (name.trim() &&text.trim() && agree) {returntrue }returnfalse }render() { const { name, text, bigText, agree } = this.statereturn ( <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) => {constnextNews= [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>