chore(i18n,curriculum): processed translations - new ukrainian (#44447)

This commit is contained in:
camperbot
2021-12-10 11:14:24 +05:30
committed by GitHub
parent 8651ee1797
commit 0473dedf47
1663 changed files with 156692 additions and 1 deletions

View File

@@ -0,0 +1,148 @@
---
id: 5a24c314108439a4d403616e
title: Доступ до пропсів за допомогою this.props
challengeType: 6
forumTopicId: 301375
dashedName: access-props-using-this-props
---
# --description--
Останні декілька завдань охоплюють основні способи передачі пропсів до дочірніх компонентів. Проте, що, коли дочірній компонент, до якого здійснюється передача пропсу, це компонент класу ES6, а не функціональний компонент? Компонент класу ES6 використовує дещо інший метод доступу до пропсів.
Щоразу посилаючись на компонент класу в його межах, використовуйте ключове слово `this`. Щоб отримати доступ до пропсів в межах компоненту класу, ви спочатку надаєте код, який використовуєте, для доступу до нього за допомогою `this`. Наприклад, якщо компонент класу ES6 має пропс з назвою `data`, ви пишете `{this.props.data}` у JSX.
# --instructions--
Візуалізуйте екземпляр компоненту `Welcome` у батьківському компоненті `App`. Надайте `Welcome` проп `name` та призначте його значенням рядка. Отримайте доступ до `name` пропу в `strong` тегах у дочірньому компоненті `Welcome`.
# --hints--
Компонент `App` повинен відображати один елемент `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(App));
return mockedComponent.children().type() === 'div';
})()
);
```
Дочірній елемент `App` має бути компонентом `Welcome`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(App));
return (
mockedComponent.children().childAt(0).name() === 'Welcome'
);
})()
);
```
Компонент `Welcome` повинен мати проп з назвою `name`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(App));
return mockedComponent.find('Welcome').props().name;
})()
);
```
Компонент `Welcome` повинен відображати рядок, який ви передаєте як `name` проп у `strong` тегах.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(App));
return (
mockedComponent.find('strong').text() ===
mockedComponent.find('Welcome').props().name
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<App />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{ /* Change code below this line */ }
<Welcome />
{ /* Change code above this line */ }
</div>
);
}
};
class Welcome extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{ /* Change code below this line */ }
<p>Hello, <strong></strong>!</p>
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class Welcome extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{ /* Change code below this line */ }
<p>Hello, <strong>{this.props.name}</strong>!</p>
{ /* Change code above this line */ }
</div>
);
}
};
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{ /* Change code below this line */ }
<Welcome name="Quincy"/>
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,82 @@
---
id: 5a24bbe0dba28a8d3cbd4c5e
title: Додавання коментарів у JSX
challengeType: 6
forumTopicId: 301376
dashedName: add-comments-in-jsx
---
# --description--
JSX це синтаксис, який компілюється у дійсний JavaScript. Деколи, задля читабельності, вам, можливо, доведеться додавати коментарі до вашого коду. Як і більшість мов програмування, JSX має власний спосіб це зробити.
Щоб розмістити коментарі у JSX, використовуйте синтаксис `{/* */}` для обрамлення навколо тексту коментаря.
# --instructions--
Редактор коду містить JSX елемент, схожий до того, який ви створили в останньому завданні. Додайте коментар будь-де в межах наданого `div` елементу, не змінюючи існуючих елементів `h1` або `p`.
# --hints--
Константа `JSX` має повернути `div` елемент.
```js
assert(JSX.type === 'div');
```
`div` повинен містити в собі тег `h1` в якості першого елементу.
```js
assert(JSX.props.children[0].type === 'h1');
```
`div` повинен містити в собі тег `p` в якості другого елементу.
```js
assert(JSX.props.children[1].type === 'p');
```
Існуючі елементи `h1` та `p` не повинні змінюватися.
```js
assert(
JSX.props.children[0].props.children === 'This is a block of JSX' &&
JSX.props.children[1].props.children === "Here's a subtitle"
);
```
`JSX` повинен використовувати відповідний синтаксис коментаря.
```js
assert(/<div>[\s\S]*{\s*\/\*[\s\S]*\*\/\s*}[\s\S]*<\/div>/.test(code));
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = (
<div>
<h1>This is a block of JSX</h1>
<p>Here's a subtitle</p>
</div>
);
```
# --solutions--
```jsx
const JSX = (
<div>
<h1>This is a block of JSX</h1>
{ /* this is a JSX comment */ }
<p>Here's a subtitle</p>
</div>);
```

View File

@@ -0,0 +1,179 @@
---
id: 5a24c314108439a4d403617e
title: Додати слухачів події
challengeType: 6
forumTopicId: 301377
dashedName: add-event-listeners
---
# --description--
Метод `componentDidMount()` також є найкращим місцем для додавання усіх слухачів події, які вам необхідні для конкретної функціональності. React надає систему синтетичних подій, яка охоплює систему початкової події, наявної у браузерах. Це означає, що синтетична система подій поводиться саме так, незалежно від браузера користувача, навіть якщо початкові події можуть поводитись по-різному між різними браузерами.
Ви вже використовуєте деякі з цих синтетичних обробників подій, на приклад `onClick()`. Система синтетичної події React є зручною для більшості взаємодій, якими ви зможете керувати у DOM елементах. Однак, якщо ви хочете прикріпити обробник подій до документа або об'єктів вікна, ви повинні зробити це напряму.
# --instructions--
Прикріпіть слухача подій у метод `componentDidMount()` для `keydown` подій і отримайте ці події, здійснивши зворотній виклик `handleKeyPress()`. Ви можете використовувати `document.addEventListener()`, який приймає подію (в лапках), в якості першого аргументу і зворотній виклик у якості другого.
Потім у `componentWillUnmount()` видаліть цього слухача подій. Ви можете передати ті ж аргументи до `document.removeEventListener()`. Хорошою практикою для використання цього методу життєвого циклу є очистка компонентів React перед тим, як вони будуть розмонтовані та знищені. Видалення слухачів подій є прикладом однієї з таких дій очистки.
# --hints--
`MyComponent` повинен відображати `div` елемент, який обгортає тег `h1`.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('div').children().find('h1').length === 1;
})()
);
```
`keydown` слухач повинен бути підключений до документу у `componentDidMount`.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const didMountString = mockedComponent
.instance()
.componentDidMount.toString();
return new RegExp(
'document.addEventListener(.|\n|\r)+keydown(.|\n|\r)+this.handleKeyPress'
).test(didMountString);
})()
);
```
`keydown` слухач повинен бути видалений з документу у `componentWillUnmount`.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const willUnmountString = mockedComponent
.instance()
.componentWillUnmount.toString();
return new RegExp(
'document.removeEventListener(.|\n|\r)+keydown(.|\n|\r)+this.handleKeyPress'
).test(willUnmountString);
})()
);
```
Як тільки компонент змонтовано, натискання клавіші `enter` змусить його оновитися та відобразити тег `h1`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const beforeState = mockedComponent.state('message');
const beforeText = mockedComponent.find('h1').text();
const pressEnterKey = () => {
mockedComponent.instance().handleKeyPress({ keyCode: 13 });
return waitForIt(() => {
mockedComponent.update();
return {
state: mockedComponent.state('message'),
text: mockedComponent.find('h1').text()
};
});
};
const afterKeyPress = await pressEnterKey();
assert(
beforeState !== afterKeyPress.state && beforeText !== afterKeyPress.text
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
this.handleEnter = this.handleEnter.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
// Change code below this line
componentDidMount() {
}
componentWillUnmount() {
}
// Change code above this line
handleEnter() {
this.setState((state) => ({
message: state.message + 'You pressed the enter key! '
}));
}
handleKeyPress(event) {
if (event.keyCode === 13) {
this.handleEnter();
}
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
this.handleKeyPress = this.handleKeyPress.bind(this);
this.handleEnter = this.handleEnter.bind(this); }
componentDidMount() {
// Change code below this line
document.addEventListener('keydown', this.handleKeyPress);
// Change code above this line
}
componentWillUnmount() {
// Change code below this line
document.removeEventListener('keydown', this.handleKeyPress);
// Change code above this line
}
handleEnter() {
this.setState((state) => ({
message: state.message + 'You pressed the enter key! '
}));
}
handleKeyPress(event) {
if (event.keyCode === 13) {
this.handleEnter();
}
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
);
}
};
```

View File

@@ -0,0 +1,112 @@
---
id: 5a24c314108439a4d4036182
title: Додавання вбудованих стилів у React
challengeType: 6
forumTopicId: 301378
dashedName: add-inline-styles-in-react
---
# --description--
В останньому завданні можна було помітити декілька інших синтаксичних відмінностей від вбудованих стилів HTML на додаток до атрибуту `style`, встановленого на об'єкт JavaScript. По-перше, у назвах певних властивостей стилю CSS використовується "горбатий регістр" (camel case). Наприклад, в останньому завданні, замість розміру шрифту `font-size` було встановлено `fontSize`. Написання слів через дефіс, як-то `font-size`, є недопустимим для властивостей об’єктів JavaScript, тому у React використовують camel case. Як правило, будь-які властивості стилю, що містять дефіс, написані в JSX camel case.
Вважається, що усі одиниці довжини властивості (наприклад, `height`, `width`, і `fontSize`) мають значення `px`, якщо не вказано інше. Наприклад, якщо виникає бажання використати `em`, то візьміть значення й одиниці у лапки, як `{fontSize: "4em"}`. Крім значень довжини, які за замовчуванням мають значення `px`, усі інші значення властивостей слід ставити у лапки.
# --instructions--
Якщо у вас є великий перелік стилів, то за стилем `object` можна закріпити константу, щоб код залишався структурованим. Опишіть константу ваших стилів як глобальну змінну у верхній частині файлу. Ініціалізуйте константу `styles` та призначте `object` з трьома властивостями стилю та їх значеннями до неї. Надайте `div` колір `purple`, розмір шрифту `40` та рамку `2px solid purple`. Потім встановіть атрибут `style` рівний константі `styles`.
# --hints--
Змінна `styles` має бути `object` з трьома властивостями.
```js
assert(Object.keys(styles).length === 3);
```
Змінна `styles` повинна мати властивість `color` налаштовану до значення `purple`.
```js
assert(styles.color === 'purple');
```
Змінна `styles` повинна мати властивість `fontSize` налаштовану до значення `40`.
```js
assert(styles.fontSize === 40);
```
Змінна `styles` повинна мати властивість `border` налаштовану до значення `2px solid purple`.
```js
assert(styles.border === '2px solid purple');
```
Компонент повинен відображати елемент `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.shallow(React.createElement(Colorful));
return mockedComponent.type() === 'div';
})()
);
```
Стилі елементу `div` мають визначатися об'єктом `styles`.
```js
assert(
(function () {
const mockedComponent = Enzyme.shallow(React.createElement(Colorful));
return (
mockedComponent.props().style.color === 'purple' &&
mockedComponent.props().style.fontSize === 40 &&
mockedComponent.props().style.border === '2px solid purple'
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Colorful />, document.getElementById('root'))
```
## --seed-contents--
```jsx
// Change code above this line
class Colorful extends React.Component {
render() {
// Change code below this line
return (
<div style={{color: "yellow", fontSize: 24}}>Style Me!</div>
);
// Change code above this line
}
};
```
# --solutions--
```jsx
const styles = {
color: "purple",
fontSize: 40,
border: "2px solid purple"
};
// Change code above this line
class Colorful extends React.Component {
render() {
// Change code below this line
return (
<div style={styles}>Style Me!</div>
);
// Change code above this line
}
};
```

View File

@@ -0,0 +1,136 @@
---
id: 5a24c314108439a4d4036174
title: Прив'язка 'this' до методу класу
challengeType: 6
forumTopicId: 301379
dashedName: bind-this-to-a-class-method
---
# --description--
На додаток до встановлення та оновлення `state`, ви також можете обрати методи для вашого компоненту класу. Метод класу зазвичай потребує використання ключового слова `this` для доступу до властивостей класу (наприклад, `state` і `props`) всередині блоку методу. Існує декілька способів, щоб дозволити вашому методу класу мати доступ до `this`.
Одним із поширених способів є прив'язка `this` у конструкторі, так щоб `this` був прив'язаним до методу класу під час ініціалізації компоненту. Ви, мабуть, помітили, що останнє завдання використовує `this.handleClick = this.handleClick.bind(this)` для його `handleClick` методу в конструкції. Тоді, коли ви викликаєте таку функцію як `this.setState()` в межах методу вашого класу, `this` посилається на клас і не буде `undefined`.
**Note:** Ключове слово `this` це один із найбільш складних аспектів JavaScript, але відіграє важливу роль у React. Хоча поводження об'єкта тут цілком нормальне, ці уроки не є місцем для детального огляду `this`, тому, будь ласка, зверніться до інших уроків, якщо наведене вище складне для сприйняття!
# --instructions--
Редактор коду має компонент `state`, який відслідковує текст. В ньому також є метод, який дозволяє вам встановити текст до `You clicked!`. Однак, метод не працює, оскільки він використовує `this` ключове слово, яке є недійсним. Виправте це відкрито пов'язуючи `this` з `handleClick()` методом в конструкторі компонента.
Далі, додайте клік обробник до елемента `button` у методі відображення. Це має запустити `handleClick()` метод, коли на кнопку клікають. Пам'ятайте, що метод, який ви передаєте `onClick` handler потребує фігурні дужки, оскільки він повинен інтерпретуватися безпосередньо як JavaScript.
Після того, як ви виконаєте наведені вище кроки, ви можете натиснути кнопку і побачити `You clicked!`.
# --hints--
`MyComponent` повинен на виході дати `div` елемент, який об'єднувати/обгортати два елементи, кнопку та елемент `h1` в даному порядку.
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).find('div').length === 1 &&
Enzyme.mount(React.createElement(MyComponent))
.find('div')
.childAt(0)
.type() === 'button' &&
Enzyme.mount(React.createElement(MyComponent))
.find('div')
.childAt(1)
.type() === 'h1'
);
```
Стан `MyComponent` має ініціалізуватись з парами ключ-значення `{ text: "Hello" }`.
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('text') === 'Hello'
);
```
Натискаючи на елемент `button`, він повинен запускати `handleClick` метод і встановлювати статус `text` to `You clicked!`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ text: 'Hello' });
return waitForIt(() => mockedComponent.state('text'));
};
const second = () => {
mockedComponent.find('button').simulate('click');
return waitForIt(() => mockedComponent.state('text'));
};
const firstValue = await first();
const secondValue = await second();
assert(firstValue === 'Hello' && secondValue === 'You clicked!');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Hello"
};
// Change code below this line
// Change code above this line
}
handleClick() {
this.setState({
text: "You clicked!"
});
}
render() {
return (
<div>
{ /* Change code below this line */ }
<button>Click Me</button>
{ /* Change code above this line */ }
<h1>{this.state.text}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Hello"
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
text: "You clicked!"
});
}
render() {
return (
<div>
<button onClick = {this.handleClick}>Click Me</button>
<h1>{this.state.text}</h1>
</div>
);
}
};
```

View File

@@ -0,0 +1,176 @@
---
id: 5a24c314108439a4d4036189
title: Зміна вбудованих CSS умовно на основі стану компонента
challengeType: 6
forumTopicId: 301380
dashedName: change-inline-css-conditionally-based-on-component-state
---
# --description--
На цьому етапі вже було опрацьовано декілька додатків умовної візуалізації та використання вбудованих стилів. Розглянемо ще один приклад, що поєднує ці дві теми. Також можна відтворити CSS умовно на основі стану компонента React. Для цього необхідно перевірити умову, і якщо вона виконана, змінити об’єкт стилів, призначений для елементів JSX методом візуалізації.
Цю парадигму важливо зрозуміти, оскільки вона є різкою зміною від більш традиційного підходу до застосування стилів шляхом безпосередньої зміни елементів DOM (що дуже часто зустрічається, наприклад, з jQuery). При такому підході необхідно відстежувати момент зміни елементів, а також безпосередньо обробляти фактичне керування. При відстежуванні змін можуть виникнути складнощі, що потенційно робить ваш інтерфейс непередбачуваним. Встановлюючи об’єкт стилю на основі умови, опишіть, що інтерфейс має виглядати як функція стану додатку. Існує чіткий потік інформації, який рухається тільки в одному напрямку. Це найбільш прийнятний метод під час написання програм з React.
# --instructions--
Редактор коду має простий керований компонент введення зі стилізованою рамкою. У разі застосування користувачем понад 15 символів тексту у полі введення, можна оформити цю рамку у червоний колір. Для перевірки, додайте умову, і, вона є дійсною, встановіть стиль введення меж для `3px solid red`. Щоб спробувати його, введіть текст у відповідне поле.
# --hints--
Компонент `GateKeeper` повинен відображати елемент `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return mockedComponent.find('div').length === 1;
})()
);
```
Компонент `GateKeeper` слід ініціалізувати ключем стану `input`, встановленим для порожнього рядка.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return mockedComponent.state().input === '';
})()
);
```
Компонент `GateKeeper` повинен відображати тег `h3` і тег `input`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return (
mockedComponent.find('h3').length === 1 &&
mockedComponent.find('input').length === 1
);
})()
);
```
Теґ `input` спочатку повинен мати стиль `1px solid black` для властивості `border`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
return (
mockedComponent.find('input').props().style.border === '1px solid black'
);
})()
);
```
Теґ `input` має бути оформлений з рамкою `3px solid red`, якщо введене значення в стані перевищує 15 символів.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
const simulateChange = (el, value) =>
el.simulate('change', { target: { value } });
let initialStyle = mockedComponent.find('input').props().style.border;
const state_1 = () => {
mockedComponent.setState({ input: 'this is 15 char' });
return waitForIt(() => mockedComponent.find('input').props().style.border);
};
const state_2 = () => {
mockedComponent.setState({
input: 'A very long string longer than 15 characters.'
});
return waitForIt(() => mockedComponent.find('input').props().style.border);
};
const style_1 = await state_1();
const style_2 = await state_2();
assert(
initialStyle === '1px solid black' &&
style_1 === '1px solid black' &&
style_2 === '3px solid red'
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<GateKeeper />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class GateKeeper extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ input: event.target.value })
}
render() {
let inputStyle = {
border: '1px solid black'
};
// Change code below this line
// Change code above this line
return (
<div>
<h3>Don't Type Too Much:</h3>
<input
type="text"
style={inputStyle}
value={this.state.input}
onChange={this.handleChange} />
</div>
);
}
};
```
# --solutions--
```jsx
class GateKeeper extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ input: event.target.value })
}
render() {
let inputStyle = {
border: '1px solid black'
};
if (this.state.input.length > 15) {
inputStyle.border = '3px solid red';
};
return (
<div>
<h3>Don't Type Too Much:</h3>
<input
type="text"
style={inputStyle}
value={this.state.input}
onChange={this.handleChange} />
</div>
);
}
};
```

View File

@@ -0,0 +1,199 @@
---
id: 5a24c314108439a4d4036166
title: Створення компонентів React
challengeType: 6
forumTopicId: 301381
dashedName: compose-react-components
---
# --description--
Оскільки завдання продовжують використовувати складніші композиції з компонентами React та JSX, слід зазначити один важливий момент. Візуалізація компонентів класу ES6 в інших компонентах нічим не відрізняється від візуалізації простих компонентів, які використовувалися в останніх кількох завданнях. Можна відтворювати елементи JSX, прості функціональні компоненти та компоненти класу ES6 в рамках інших компонентів.
# --instructions--
У редакторі коду компонент `TypesOfFood` вже відображає компонент з назвою `Vegetables`. Також, в останньому завданні є компонент `Fruits`.
Створіть два компоненти всередині `Fruits`: спочатку - `NonCitrus`, потім - `Citrus`. Обидва ці компоненти доступні у налаштуваннях за замовчуванням. Далі, додайте компонент класу `Fruits` до компонента `TypesOfFood` під заголовком `h1` та над `Vegetables`. У результаті отримуємо низку вкладених компонентів, яка використовує два різних типи компонентів.
# --hints--
Компонент `TypesOfFood` повинен показувати одинарний елемент `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().type() === 'div';
})()
);
```
Компонент `TypesOfFood` повинен показувати компонент `Fruits`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(1).name() === 'Fruits';
})()
);
```
Компонент `Fruits` повинен показувати компоненти `NonCitrus` та `Citrus`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return (
mockedComponent.find('Fruits').children().find('NonCitrus').length ===
1 &&
mockedComponent.find('Fruits').children().find('Citrus').length === 1
);
})()
);
```
Компонент `TypesOfFood` повинен показувати компонент `Vegetables` нижче від компонента `Fruits`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(2).name() === 'Vegetables';
})()
);
```
# --seed--
## --before-user-code--
```jsx
class NonCitrus extends React.Component {
render() {
return (
<div>
<h4>Non-Citrus:</h4>
<ul>
<li>Apples</li>
<li>Blueberries</li>
<li>Strawberries</li>
<li>Bananas</li>
</ul>
</div>
);
}
};
class Citrus extends React.Component {
render() {
return (
<div>
<h4>Citrus:</h4>
<ul>
<li>Lemon</li>
<li>Lime</li>
<li>Orange</li>
<li>Grapefruit</li>
</ul>
</div>
);
}
};
class Vegetables extends React.Component {
render() {
return (
<div>
<h2>Vegetables:</h2>
<ul>
<li>Brussel Sprouts</li>
<li>Broccoli</li>
<li>Squash</li>
</ul>
</div>
);
}
};
```
## --after-user-code--
```jsx
ReactDOM.render(<TypesOfFood />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class Fruits extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>Fruits:</h2>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
<Vegetables />
</div>
);
}
};
```
# --solutions--
```jsx
class Fruits extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h2>Fruits:</h2>
{ /* Change code below this line */ }
<NonCitrus />
<Citrus />
{ /* Change code above this line */ }
</div>
)
}
}
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{ /* Change code below this line */ }
<Fruits />
{ /* Change code above this line */ }
<Vegetables />
</div>
);
}
};
```

View File

@@ -0,0 +1,110 @@
---
id: 5a24bbe0dba28a8d3cbd4c5d
title: Створіть складний JSX елемент
challengeType: 6
forumTopicId: 301382
dashedName: create-a-complex-jsx-element
---
# --description--
Останнє завдання було простим прикладом JSX, але JSX може також позначати складніший HTML.
Треба пам'ятати, що вкладений JSX повинен показувати одинарний елемент.
Цей один батьківський елемент містить всі інші рівні вкладених елементів.
Наприклад, декілька елементів JSX, записані як елементи одного рівня, без батьківського оболонкового елементу не будуть компілюватися в код на іншій мові програмування.
Наприклад:
**Дійсний JSX:**
```jsx
<div>
<p>Paragraph One</p>
<p>Paragraph Two</p>
<p>Paragraph Three</p>
</div>
```
**Недійсний JSX:**
```jsx
<p>Paragraph One</p>
<p>Paragraph Two</p>
<p>Paragraph Three</p>
```
# --instructions--
Визначте нову константу `JSX`, що відображає `div`, котрий містить в собі такі елементи у порядку:
`h1`, `p` і невпорядкований список, який містить в собі три елементи `li`. Ви можете включити будь-який текст, який забажаєте, в кожному з елементів.
**Примітка:** При візуалізації багатозначних елементів як тут, можна взяти їх усі в дужки, однак це не є обов'язковим. Зверніть також увагу на те, що у цьому завданні використовується тег `div` для поєднання всіх дочірніх елементів в межах одинарного батьківського елементу. При видаленні `div`, JSX вже не буде компілюватися. Майте це на увазі, оскільки це саме буде застосовано при поверненні елементів JSX до компонентів React.
# --hints--
Константа `JSX` повинна повертати елемент `div`.
```js
assert(JSX.type === 'div');
```
`div` повинен містити в собі теґ `h1` в якості першого елементу.
```js
assert(JSX.props.children[0].type === 'h1');
```
`div` повинен містити в собі теґ `p` в якості другого елементу.
```js
assert(JSX.props.children[1].type === 'p');
```
`div` повинен містити в собі теґ `ul` в якості третього елементу.
```js
assert(JSX.props.children[2].type === 'ul');
```
`ul` повинен містити в собі три елементи `li`.
```js
assert(
JSX.props.children
.filter((ele) => ele.type === 'ul')[0]
.props.children.filter((ele) => ele.type === 'li').length === 3
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
```
# --solutions--
```jsx
const JSX = (
<div>
<h1>Hello JSX!</h1>
<p>Some info</p>
<ul>
<li>An item</li>
<li>Another item</li>
<li>A third item</li>
</ul>
</div>);
```

View File

@@ -0,0 +1,134 @@
---
id: 5a24c314108439a4d4036164
title: Створення компоненту за допомогою композиції
challengeType: 6
forumTopicId: 301383
dashedName: create-a-component-with-composition
---
# --description--
Тепер ми поглянемо, як ми можемо скласти декілька React разом. Уявіть, що ви створюєте додаток і вже створили три компоненти: `Navbar`, `Dashboard`, і `Footer`.
Щоб скласти їх разом, ви можете створити компонент `App` *parent*, який відображає кожен з цих трьох компонентів як *children*. Щоб відобразити компонент як дочірній у React компоненті, ви включаєте в перелік ім'я компоненту, написане як власний HTML-тег у JSX. Для прикладу, у методі `render` ви можете написати:
```jsx
return (
<App>
<Navbar />
<Dashboard />
<Footer />
</App>
)
```
Коли React стикається із власним HTML-тегом, який посилається на інший компонент (ім'я компоненту взято в дужки у `< />` як в цьому прикладі), він відображає примітку для цього компоненту у місці розташування тегу. Це повинно відображати батьківські/дочірні зв'язки між компонентом `App` і `Navbar`, `Dashboard` та `Footer`.
# --instructions--
У редакторі коду є простий функціональний компонент, який називається `ChildComponent` і компонент класу, який називається `ParentComponent`. Складіть їх обидвох разом, використовуючи відображення `ChildComponent` всередині `ParentComponent`. Пересвідчіться, що закрили тег `ChildComponent` за допомогою навскісної риски перед ним.
**Note:** `ChildComponent` визначається за допомогою функції стрілки ES6, тому що це досить поширена практика під час використання React. Проте, знайте, що це - просто функція. Якщо ви не знайомі з синтаксисом функції зі стрілками, будь ласка, зверніться до розділу JavaScript.
# --hints--
React компонент повинен повертати одинарний `div` елемент.
```js
assert(
(function () {
var shallowRender = Enzyme.shallow(React.createElement(ParentComponent));
return shallowRender.type() === 'div';
})()
);
```
Компонент повинен повертати два вкладених елементи.
```js
assert(
(function () {
var shallowRender = Enzyme.shallow(React.createElement(ParentComponent));
return shallowRender.children().length === 2;
})()
);
```
Компонент повинен повертати `ChildComponent` в якості другого дочірнього елементу.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ParentComponent));
return (
mockedComponent.find('ParentComponent').find('ChildComponent').length ===
1
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ParentComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const ChildComponent = () => {
return (
<div>
<p>I am the child</p>
</div>
);
};
class ParentComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>I am the parent</h1>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
const ChildComponent = () => {
return (
<div>
<p>I am the child</p>
</div>
);
};
class ParentComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>I am the parent</h1>
{ /* Change code below this line */ }
<ChildComponent />
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,220 @@
---
id: 5a24c314108439a4d4036179
title: Створення контрольованої форми
challengeType: 6
forumTopicId: 301384
dashedName: create-a-controlled-form
---
# --description--
Останнє завдання показало, що React може контролювати внутрішній стан певних елементів, як-то `input` та `textarea`, що робить їх контрольованими компонентами. Це також стосується і інших елементів форм, включно із звичайним елементом HTML `form`.
# --instructions--
Компонент `MyForm` встановлено за допомогою порожнього `form` з ініціалізованим обробником. Ініціалізований обробник буде викликано тоді, коли форма буде відправлена.
Ми додали кнопку, яка відправляє форму. Ви можете побачити, що для неї налаштовано `type` для `submit`, що означає, що кнопка контролює форму. Додайте `input` елемент у `form` і встановіть його `value` та `onChange()` атрибути, як в останньому завданні. Ви повинні виконати метод `handleSubmit`, щоб він міг встановити властивість компоненту `submit` для поточного вхідного значення у локальному `state`.
**Note:** Ви також повинні викликати `event.preventDefault()` у ініціалізованому обробнику, щоб запобігти поводженню об'єкта за замовчуванням, яке оновить веб-сторінку. Для зручності учнів, поводження об'єкту за замовчуванням було вимкнено, щоб запобігти оновленню та скидуванню коду завдання.
Нарешті, створіть тег `h1` після `form`, який відображає значення `submit` зі стану компоненту `state`. Далі ви можете надрукувати щось в формі та клацнути кнопку (або натиснути клавішу Enter), після чого повинні будете побачити результат виведення на сторінці.
# --hints--
`MyForm` повинен повертати елемент `div`, який містить в собі `form` та тег `h1`. Форма повинна включати в себе елементи `input` та `button`.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
return (
mockedComponent.find('div').children().find('form').length === 1 &&
mockedComponent.find('div').children().find('h1').length === 1 &&
mockedComponent.find('form').children().find('input').length === 1 &&
mockedComponent.find('form').children().find('button').length === 1
);
})()
);
```
Стан `MyForm` повинен ініціалізувати за допомогою `input` та `submit` властивості, для обох встановлені у порожніх рядках.
```js
assert(
Enzyme.mount(React.createElement(MyForm)).state('input') === '' &&
Enzyme.mount(React.createElement(MyForm)).state('submit') === ''
);
```
Також елемент `input` повинен відображати значення `input`, що знаходиться в стані компоненту.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
const _1 = () => {
mockedComponent.setState({ input: '' });
return mockedComponent.state('input');
};
const _2 = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return {
state: mockedComponent.state('input'),
inputVal: mockedComponent.find('input').props().value
};
};
const before = _1();
const after = _2();
assert(
before === '' &&
after.state === 'TestInput' &&
after.inputVal === 'TestInput'
);
})();
```
Запит форми має бути виконано із `handleSubmit`, який повинен встановлювати `submit` властивість у стані, рівному поточному введенню.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
const _1 = () => {
mockedComponent.setState({ input: '' });
mockedComponent.setState({ submit: '' });
mockedComponent
.find('input')
.simulate('change', { target: { value: 'SubmitInput' } });
return mockedComponent.state('submit');
};
const _2 = () => {
mockedComponent.find('form').simulate('submit');
return mockedComponent.state('submit');
};
const before = _1();
const after = _2();
assert(before === '' && after === 'SubmitInput');
})();
```
`handleSubmit` повинен викликати `event.preventDefault`
```js
assert(
__helpers.isCalledWithNoArgs(
'event.preventDefault',
MyForm.prototype.handleSubmit.toString()
)
);
```
Заголовок `h1` повинен відображати значення `submit` поля зі стану компонента.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
const _1 = () => {
mockedComponent.setState({ input: '' });
mockedComponent.setState({ submit: '' });
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return mockedComponent.find('h1').text();
};
const _2 = () => {
mockedComponent.find('form').simulate('submit');
return mockedComponent.find('h1').text();
};
const before = _1();
const after = _2();
assert(before === '' && after === 'TestInput');
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyForm />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
handleSubmit(event) {
// Change code below this line
// Change code above this line
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
{/* Change code below this line */}
{/* Change code above this line */}
<button type='submit'>Submit!</button>
</form>
{/* Change code below this line */}
{/* Change code above this line */}
</div>
);
}
}
```
# --solutions--
```jsx
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
handleSubmit(event) {
event.preventDefault();
this.setState(state => ({
submit: state.input
}));
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input value={this.state.input} onChange={this.handleChange} />
<button type='submit'>Submit!</button>
</form>
<h1>{this.state.submit}</h1>
</div>
);
}
}
```

View File

@@ -0,0 +1,150 @@
---
id: 5a24c314108439a4d4036178
title: Створення контрольованого вводу
challengeType: 6
forumTopicId: 301385
dashedName: create-a-controlled-input
---
# --description--
Ваш додаток може мати набагато складніші взаємодії між `state` і відображеним інтерфейсом користувача. Для прикладу, керування елементами форми для введення тексту, такими як `input` та `textarea`, зберігає їх власний стан у DOM в якості типу користувача. За допомогою React, ви можете перемістити цей незмінний стан до компоненту React `state`. Вхід користувача стає частиною програми `state`, отож React контролює значення цього поля вводу. Зазвичай, якщо у вас є компоненти React із полями вводу, в які користувач може друкувати, це буде контрольована форму вводу.
# --instructions--
Редактор коду має каркас компонента із назвою `ControlledInput` для створення контрольованого `input` елементу. `state` компоненту вже ініціалізовано із `input` властивістю, яка містить порожній рядок. Це значення являє собою текст, який друкує користувач у поле `input`.
По-перше, створіть метод із назвою `handleChange()`, який має параметр із назвою `event`. Коли викликається метод, він отримує `event` об'єкт, що містить в собі рядок тексту з `input` елементу. Ви можете отримати доступ до цього рядка з `event.target.value` всередині методу. Оновіть `input` властивості `state` компонента за допомогою нового рядка.
У методі `render` створіть `input` елемент над тегом `h4`. Додайте атрибут `value`, який дорівнює властивості `input` `state` компоненту. Тоді додайте `onChange()` набір обробника подій до методу `handleChange()`.
Коли ви друкуєте у полі вхідних даних, цей текст обробляється методом `handleChange()`, встановленим як властивість `input` у локальному `state`, і відображається в якості значення у `input` полі на сторінці. Компонент `state` є єдиним джерело істини стосовно вхідних даних.
Останнє, проте важливе - не забудьте додати необхідні прив'язки до конструктора.
# --hints--
`ControlledInput` повинен повертати `div` елемент, який містить в собі `input` та тег `p`.
```js
assert(
Enzyme.mount(React.createElement(ControlledInput))
.find('div')
.children()
.find('input').length === 1 &&
Enzyme.mount(React.createElement(ControlledInput))
.find('div')
.children()
.find('p').length === 1
);
```
Стан `ControlledInput` має ініціалізуватись із допомогою властивості `input` встановленої у порожній рядок.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(ControlledInput)).state('input'),
''
);
```
Друкування у вхідному елементі повинне оновлювати стан і значення вхідних даних, а `p` елемент повинен відображати цей стан згідно з тим, що ви друкуєте.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(ControlledInput));
const _1 = () => {
mockedComponent.setState({ input: '' });
return waitForIt(() => mockedComponent.state('input'));
};
const _2 = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return waitForIt(() => ({
state: mockedComponent.state('input'),
text: mockedComponent.find('p').text(),
inputVal: mockedComponent.find('input').props().value
}));
};
const before = await _1();
const after = await _2();
assert(
before === '' &&
after.state === 'TestInput' &&
after.text === 'TestInput' &&
after.inputVal === 'TestInput'
);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ControlledInput />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
// Change code below this line
// Change code above this line
}
// Change code below this line
// Change code above this line
render() {
return (
<div>
{ /* Change code below this line */}
{ /* Change code above this line */}
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
```
# --solutions--
```jsx
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
render() {
return (
<div>
<input
value={this.state.input}
onChange={this.handleChange} />
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
```

View File

@@ -0,0 +1,102 @@
---
id: 5a24c314108439a4d4036163
title: Створення компоненту React
challengeType: 6
forumTopicId: 301386
dashedName: create-a-react-component
---
# --description--
Інший спосіб, щоб визначити React компонент - за допомогою синтаксису ES6 `class`. В наступному прикладі `Kitten` розширює `React.Component`:
```jsx
class Kitten extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<h1>Hi</h1>
);
}
}
```
Це створює клас ES6 `Kitten`, який розширює клас `React.Component`. Отож, клас `Kitten` тепер має доступ до багатьох корисних функцій React, як наприклад, локальний стан та пастки життєвого циклу. Не хвилюйтеся, якщо ви ще не знайомі з цими термінами, вони будуть детально розкриті у наступних завданнях. Також, зверніть увагу на те, що клас `Kitten` має `constructor` визначений через `super()`. Він використовує `super()`, щоб викликати конструктора батьківського класу, в цьому випадку `React.Component`. Конструктор - це спеціальний метод, який використовується під час ініціалізації об'єктів, які було створено за допомогою ключового слова `class`. Найкраща практика для виклику компоненту `constructor` - використати `super`, а потім провести `props` до обидвох. Це дозволяє впевнитись, що компонент ініціалізовано належним чином. Поки що відомо, що це - стандарт для цього коду. Скоро ви побачите інше використання конструктора, а також `props`.
# --instructions--
`MyComponent` визначається в редакторі коду за допомогою синтаксису класів. Завершіть запис методу `render`, щоб він повернув елемент `div`, який містить в собі `h1` з текстом `Hello React!`.
# --hints--
React компонент повинен повертати `div` елемент.
```js
assert(Enzyme.shallow(React.createElement(MyComponent)).type() === 'div');
```
Повернений `div` повинен відображати заголовок `h1` в собі.
```js
assert(
/<div><h1>.*<\/h1><\/div>/.test(
Enzyme.shallow(React.createElement(MyComponent)).html()
)
);
```
Заголовок `h1` повинен містити в собі рядок `Hello React!`.
```js
assert(
Enzyme.shallow(React.createElement(MyComponent)).html() ===
'<div><h1>Hello React!</h1></div>'
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
// Change code below this line
// Change code above this line
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
// Change code below this line
return (
<div>
<h1>Hello React!</h1>
</div>
);
// Change code above this line
}
};
```

View File

@@ -0,0 +1,57 @@
---
id: 587d7dbc367417b2b2512bb1
title: Створення простого JSX елементу
challengeType: 6
forumTopicId: 301390
dashedName: create-a-simple-jsx-element
---
# --description--
React - це бібліотека з відкритим вихідним кодом, яка підтримується Facebook. Це гарний інструмент для візуалізації інтерфейсу користувача (UI) у сучасних вебпрограмах.
React використовує розширення синтаксису JSX з JavaScript, що дозволяє писати HTML код прямо у JavaScript. JSX надає нам кілька переваг. З ним зберігаються усі можливості програмування JavaScript у HTML, а також зберігається читабельність коду. Здебільшого, JSX подібний до HTML, який ви вже вивчали, хоча є декілька ключових відмінностей, які ми розглянемо далі.
Наприклад, оскільки JSX є синтаксичним розширенням для JavaScript, ви можете писати JavaScript безпосередньо в JSX. Для цього, просто напишіть код у фігурних дужках, якщо він має сприйматися як JavaScript: `{ 'this is treated as JavaScript code' }`. Це знадобиться вам у майбутніх завданнях.
Однак, оскільки JSX не справжній JavaScript, JSX код потрібно спочатку скомпілювати у JavaScript. Для цього часто використовують компілятор Babel (компілятор типу "вихідний код у вихідний код"). Щоб вам було зручніше, ми вже додали його перед початком завдань. Якщо у вас буде неправильний синтаксис JSX, ви побачите, що перший тест у завданні видасть помилку.
Варто зазначити, що завдання непомітно звертається до `ReactDOM.render(JSX, document.getElementById('root'))`. Цей виклик функції розміщує JSX у просте вираження DOM, що створює React. Далі React використовує статистику власних DOM, щоб оптимізувати оновлення конкретних частин у конкретному DOM.
# --instructions--
Поточний код використовує JSX, щоб призначити елемент `div` константі `JSX`. Замініть `div` на елемент `h1` та додайте текст `Hello JSX!` у нього.
# --hints--
Константа `JSX` має повернути елемент `h1`.
```js
assert(JSX.type === 'h1');
```
Тег `h1` має містити текст `Hello JSX!`
```js
assert(Enzyme.shallow(JSX).contains('Hello JSX!'));
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = <div></div>;
```
# --solutions--
```jsx
const JSX = <h1>Hello JSX!</h1>;
```

View File

@@ -0,0 +1,134 @@
---
id: 5a24c314108439a4d4036170
title: Створити компонент Stateful
challengeType: 6
forumTopicId: 301391
dashedName: create-a-stateful-component
---
# --description--
Однією із найважливіших тем у React є `state`. Стан складається з будь-яких даних, про які потрібно знати, і які можуть з часом зазнавати змін. Ви хочете, щоб ваші програми відповіли на зміни стану і представили оновлений інтерфейс, коли це необхідно. React пропонує чудове рішення для управління станом сучасних веб-додатків.
Ви створюєте стан в компоненті React, вказавши про властивість `state` для класу компоненту з його `constructor`. Це ініціалізує компонент з `state`, коли він створюється. Властивість `state` повинна бути встановлена на JavaScript `object`. Іншими словами, це виглядає так:
```jsx
this.state = {
}
```
У вас є доступ до предмета `state` протягом усього життя вашого компонента. Ви можете його оновити, візуалізувати його в своєму інтерфейсі, і передати його як протоколи до споріднених документів. Об'єкт `state` може бути як простим, так і складним, в залежності від того, який вам потрібен. Зверніть увагу, ви повинні створити клас компонентів, розширивши `React.Component` для того, щоб створити такий як цей `state`.
# --instructions--
У кодовому редакторі є компонент, який намагається візуалізувати властивість `name` з його `state`. Однак, `state` є невизначеним. Ініціалізуйте компонент з `state` в `constructor` і призначте ваше ім'я до властивості `name`.
# --hints--
`StatefulComponent` повинен існувати і візуалізуватися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
return mockedComponent.find('StatefulComponent').length === 1;
})()
);
```
`StatefulComponent` повинен візуалізувати `div` і `h1` елемент.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
return (
mockedComponent.find('div').length === 1 &&
mockedComponent.find('h1').length === 1
);
})()
);
```
Стан `StatefulComponent` повинен бути ініціалізований з властивістю `name`, яка встановлена на рядок.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
const initialState = mockedComponent.state();
return (
typeof initialState === 'object' && typeof initialState.name === 'string'
);
})()
);
```
Значення `name` у стані `StatefulComponent` повинне візуалізувати елемент `h1`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(StatefulComponent)
);
const initialState = mockedComponent.state();
return mockedComponent.find('h1').text() === initialState.name;
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<StatefulComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class StatefulComponent extends React.Component {
constructor(props) {
super(props);
// Only change code below this line
// Only change code above this line
}
render() {
return (
<div>
<h1>{this.state.name}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class StatefulComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp!'
}
}
render() {
return (
<div>
<h1>{this.state.name}</h1>
</div>
);
}
};
```

View File

@@ -0,0 +1,102 @@
---
id: 5a24c314108439a4d4036162
title: Створення функціонального компонента Stateless
challengeType: 6
forumTopicId: 301392
dashedName: create-a-stateless-functional-component
---
# --description--
Компоненти є ядром React. Все в React є компонентом, і тут ви дізнаєтеся, як їх створити.
Існує два шляхи створення компонента React. Першим способом є використання функції JavaScript. Визначення компонента таким чином створює * stateless functional component*. Поняття стану у додатку буде розглянуто у подальших завданнях. Поки що думайте про компонент stateless як про той, який може отримувати дані та надавати їх, але не керує та не відстежує зміни цих даних. (Ми розглянемо інший спосіб створення компонента React у наступному завданні.)
Щоб створити компонент з функцією, варто лише написати функцію JavaScript, яка повертає або JSX, або `null`. Важливо відзначити, що для React важливим є початок назви вашої функції з великої літери. Ось приклад функціонального компонента stateless, який призначає клас HTML у JSX:
```jsx
const DemoComponent = function() {
return (
<div className='customClass' />
);
};
```
Після перенесення `<div>` матиме клас CSS `customClass`.
Оскільки компонент JSX представляє HTML, ви маєте можливість об'єднати кілька компонентів разом, щоб створити більш складну сторінку HTML. Це одна з ключових переваг компонентної архітектури, яку надає React. Це дозволяє складати інтерфейс користувача з багатьох окремих, ізольованих компонентів. Це полегшує створення та обслуговування складних інтерфейсів користувача.
# --instructions--
Редактор коду має функцію `MyComponent`. Виконайте цю функцію, щоб вона повернула єдиний елемент `div`, який містить деякий рядок тексту.
** Примітка.** Текст вважається дочірнім елементом `div`, тому ви не зможете використовувати тег із самозакриттям.
# --hints--
`MyComponent` має повертати JSX.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.length === 1;
})()
);
```
`MyComponent` має повертати елемент `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.children().type() === 'div';
})()
);
```
Елемент `div` повинен містити рядок тексту.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('div').text() !== '';
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const MyComponent = function() {
// Change code below this line
// Change code above this line
}
```
# --solutions--
```jsx
const MyComponent = function() {
// Change code below this line
return (
<div>
Demo Solution
</div>
);
// Change code above this line
}
```

View File

@@ -0,0 +1,62 @@
---
id: 5a24c314108439a4d4036160
title: Визначення HTML-класу в JSX
challengeType: 6
forumTopicId: 301393
dashedName: define-an-html-class-in-jsx
---
# --description--
Тепер, коли ви освоїлися з кодуванням у JSX, вам може бути цікаво, чим він відрізняється від HTML.
Поки що може здатися, що HTML та JSX абсолютно однакові.
Однією з ключових відмінностей JSX є те, що ви більше не можете використовувати слово `class` для визначення класів HTML. Це тому, що `class` є зарезервованим словом в JavaScript. Натомість в JSX використовується `className`.
Фактично, умова іменування для всіх атрибутів HTML та посилань на події в JSX стає camelCase. Наприклад, подія кліку миші в JSX - це `onClick` замість `onclick`. Аналогічно, `onchange` стає `onChange`. Хоча це незначна відмінність, про це слід пам'ятати в майбутньому.
# --instructions--
Застосуйте клас `myDiv` до `div`, наданого у коді JSX.
# --hints--
Константа `JSX` має повернути елемент `div`.
```js
assert.strictEqual(JSX.type, 'div');
```
`div` повинен мати клас `myDiv`.
```js
assert.strictEqual(JSX.props.className, 'myDiv');
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = (
<div>
<h1>Add a class to this div</h1>
</div>
);
```
# --solutions--
```jsx
const JSX = (
<div className = 'myDiv'>
<h1>Add a class to this div</h1>
</div>);
```

View File

@@ -0,0 +1,147 @@
---
id: 5a24c314108439a4d403618b
title: Надання однорівневим елементам унікального атрибута key
challengeType: 6
forumTopicId: 301394
dashedName: give-sibling-elements-a-unique-key-attribute
---
# --description--
Останнє завдання показало як метод `map` використовується для динамічної візуалізації ряду елементів на основі вводу користувача. Однак у цьому прикладі була пропущена важлива частина. Коли ви створюєте масив елементів, кожний з них потребує атрибут `key` з унікальним значенням. React використовує такі ключі, щоб відстежувати, які елементи додаються, змінюються або видаляються. Це допомагає зробити процес повторної візуалізації більш ефективним, коли список змінюється будь-яким чином.
**Примітка:** Ключі мають бути унікальними лише між елементами одного рівня, вони не повинні бути глобально унікальними в вашому застосунку.
# --instructions--
Редактор коду має масив з деякими інтерфейсними фреймворками та функціональним компонентом без збереження стану з назвою `Frameworks()`. `Frameworks()` потребує перетворення масиву у невпорядкований список, так само як в останньому завданні. Завершіть запис функції зворотного виклику `map`, щоб повернути елемент `li` для кожного фреймворку в масиві `frontEndFrameworks`. Цього разу переконайтесь, що надали кожному `li` атрибут `key` з унікальним значенням. Елементи `li` також повинні містити текст з `frontEndFrameworks`.
Зазвичай, ви хочете зробити ключем те, що однозначно ідентифікує візуалізований елемент. В крайньому разі може бути використаний індекс масиву, але зазвичай слід намагатися використовувати унікальну ідентифікацію.
# --hints--
Компонент `Frameworks` має існувати та відображатися на сторінці.
```js
assert(
Enzyme.mount(React.createElement(Frameworks)).find('Frameworks').length === 1
);
```
`Frameworks` має відображати елемент `h1`.
```js
assert(Enzyme.mount(React.createElement(Frameworks)).find('h1').length === 1);
```
`Frameworks` має відображати елемент `ul`.
```js
assert(Enzyme.mount(React.createElement(Frameworks)).find('ul').length === 1);
```
Тег `ul` має відображати 6 дочірніх елементів `li`.
```js
assert(
Enzyme.mount(React.createElement(Frameworks)).find('ul').children().length ===
6 &&
Enzyme.mount(React.createElement(Frameworks))
.find('ul')
.childAt(0)
.name() === 'li' &&
Enzyme.mount(React.createElement(Frameworks)).find('li').length === 6
);
```
Кожний елемент зі списку повинен мати унікальний атрибут `key`.
```js
assert(
(() => {
const ul = Enzyme.mount(React.createElement(Frameworks)).find('ul');
const keys = new Set([
ul.childAt(0).key(),
ul.childAt(1).key(),
ul.childAt(2).key(),
ul.childAt(3).key(),
ul.childAt(4).key(),
ul.childAt(5).key()
]);
return keys.size === 6;
})()
);
```
Кожний елемент зі списку повинен містити текст із `frontEndFrameworks`.
```js
assert(
(() => {
const li = Enzyme.mount(React.createElement(Frameworks))
.find('ul')
.children();
return [...Array(5)].every((_, i) =>
frontEndFrameworks.includes(li.at(i).text())
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Frameworks />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const frontEndFrameworks = [
'React',
'Angular',
'Ember',
'Knockout',
'Backbone',
'Vue'
];
function Frameworks() {
const renderFrameworks = null; // Change this line
return (
<div>
<h1>Popular Front End JavaScript Frameworks</h1>
<ul>
{renderFrameworks}
</ul>
</div>
);
};
```
# --solutions--
```jsx
const frontEndFrameworks = [
'React',
'Angular',
'Ember',
'Knockout',
'Backbone',
'Vue'
];
function Frameworks() {
const renderFrameworks = frontEndFrameworks.map((fw, i) => <li key={i}>{fw}</li>);
return (
<div>
<h1>Popular Front End JavaScript Frameworks</h1>
<ul>
{renderFrameworks}
</ul>
</div>
);
};
```

View File

@@ -0,0 +1,103 @@
---
id: 5a24c314108439a4d4036181
title: Представляємо вбудовані стилі
challengeType: 6
forumTopicId: 301395
dashedName: introducing-inline-styles
---
# --description--
Наявні інші складні концепції, що додають потужних можливостей для вашого коду React. Проте вас може зацікавити, більш проста задача, як стилізувати ті JSX елементи, які ви створюєте в React. Ви, можливо, знаєте, що це не буде в точності теж саме, що і робота з HTML, тому що [ це спосіб, за допомогою якого ви застосовуєте класи до JSX елементів](/learn/front-end-development-libraries/react/define-an-html-class-in-jsx).
Якщо ви імпортували стилі із таблиці стилів, то не суттєво відрізняється. Ви застосовуєте класи для вашого JSX елементу, користуючись `className` атрибута, також використовуєте стилі класу до вашої таблиці стилів. Інший варіант це застосувати вбудовані стилі, які дуже поширені в розробці ReactJS.
Ви застосовуєте вбудовані стилі до елементів JSX подібно до того, як це робиться в HTML, але з деякими відмінностями в JSX. Ось приклад вбудованого стилю в HTML:
```jsx
<div style="color: yellow; font-size: 16px">Mellow Yellow</div>
```
Елементи JSX використовують атрибут `style`, але через шлях передачі JSX ви не можете встановити значення `string`. Замість цього зробіть його таким самим, як JavaScript `object`. Наприклад:
```jsx
<div style={{color: "yellow", fontSize: 16}}>Mellow Yellow</div>
```
Зверніть увагу, як ми зібрали значення `fontSize` у шрифті? Це тому, що React не буде приймати ключі кеба-регістру в об'єкті стилю. React може застосувати ім'я з правильними властивостями для нас у HTML.
# --instructions--
Додайте атрибут `style` до `div` в редактор коду, щоб надати тексту червоного кольору а шрифту розміру `72px`.
Зверніть увагу, ви можете за необхідністю встановити розмір шрифту як номер, пропускаючи одиниці виміру `px`, або записати це як `72px`.
# --hints--
Компонент повинен відображати елемент `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
return mockedComponent.children().type() === 'div';
})()
);
```
Елемент `div` повинен мати `red` колір.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
return mockedComponent.children().props().style.color === 'red';
})()
);
```
Елемент `div` повинен мати розмір шрифту `72px`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
return (
mockedComponent.children().props().style.fontSize === 72 ||
mockedComponent.children().props().style.fontSize === '72' ||
mockedComponent.children().props().style.fontSize === '72px'
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Colorful />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class Colorful extends React.Component {
render() {
return (
<div>Big Red</div>
);
}
};
```
# --solutions--
```jsx
class Colorful extends React.Component {
render() {
return (
<div style={{color: "red", fontSize: 72}}>Big Red</div>
);
}
};
```

View File

@@ -0,0 +1,75 @@
---
id: 5a24c314108439a4d4036161
title: Дізнайтеся про теги Self-Closing JSX
challengeType: 6
forumTopicId: 301396
dashedName: learn-about-self-closing-jsx-tags
---
# --description--
До цього часу ви бачили, як JSX відрізняється від HTML ключовим чином за допомогою `className` vs. `class`для визначення класів HTML.
Іншою важливою відмінністю JSX від HTML є уявлення про теги, які самі закриваються.
У HTML, майже всі теги мають як тег, який містить тиги які відкривають, та закривають теги:`<div></div>` тег, що закриває, завжди має скісну риску перед назвою тегу, який ви закриваєте. Проте, в HTML є особливі випадки, так звані " теги що самі закриваються", або теги, які не вимагають відкриття та закриття тегів, перш ніж інший тег може початися.
Наприклад, тег розриву рядка можна записати у вигляді`<br>` або як `<br />`, але ніколи не слід писати як `<br></br>`, оскільки він не містить змісту.
У JSX правила трохи відрізняються. Кожен елемент JSX може бути записаний з тегом, який сам закривається і будь-який елемент повинен бути закритий. Тег розриву рядка, наприклад, завжди повинен бути записаний як`<br />`, щоб бути дійсним JSX, який може змінюватися. `<div>`, з іншого боку, може бути записаний як `<div />` або `<div></div>`. Різниця в тому, що в першій версії синтаксису немає можливості включити що-небудь до` <div /> `. У наступних завданнях ви побачите, що цей синтаксис корисний при передачі компонентів React.
# --instructions--
Виправляйте помилки в редакторі коду так, щоб вони були дійсними JSX і успішно транслювалися. Переконайтеся, що ви не міняєте вміст - вам потрібно тільки закрити теги там, де вони потрібні.
# --hints--
Константа `JSX` повинна повертатися як `div`.
```js
assert.strictEqual(JSX.type, 'div');
```
`div` повинен містити в собі такі елементи `br`.
```js
assert(Enzyme.shallow(JSX).find('br').length === 1);
```
`div` повинен містити в собі такі елементи як `hr`.
```js
assert(Enzyme.shallow(JSX).find('hr').length === 1);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(JSX, document.getElementById('root'))
```
## --seed-contents--
```jsx
const JSX = (
<div>
<h2>Welcome to React!</h2> <br >
<p>Be sure to close all tags!</p>
<hr >
</div>
);
```
# --solutions--
```jsx
const JSX = (
<div>
<h2>Welcome to React!</h2> <br />
<p>Be sure to close all tags!</p>
<hr />
</div>
);
```

View File

@@ -0,0 +1,187 @@
---
id: 5a24c314108439a4d4036180
title: Оптимізація Re-Renders з shouldComponentUpdate
challengeType: 6
forumTopicId: 301398
dashedName: optimize-re-renders-with-shouldcomponentupdate
---
# --description--
Тепер, коли компонент переходить у новий `state` або отримує новий `props`, він повторно перетворює себе і свої дочірні елементи. Як правило, це нормально. Але React надає метод, що можна викликати, коли дочірній елемент отримує новий `state` або `props` і встановлює, чи потрібно його оновлювати. Цей метод називається `shouldComponentUpdate()`, він створює параметри `nextProps` й `nextState`.
Цей метод допомагає оптимізувати роботу програми. Наприклад, при звичайному процесі компоненти ререндеряться, коли вони отримують нові `props`, навіть якщо `props` не змінювались. Можете скористатися `shouldComponentUpdate()`, щоб запобігти цьому, шляхом порівняння `props`. Метод має видати `boolean` значення, що говорить React, чи потрібно обновити компонент. Можете порівняти поточні props (`this.props`) з новими props (`nextProps`), щоб визначити, чи потрібно їх оновити, та відповідно повернути сигнал `true` або `false`.
# --instructions--
Метод `shouldComponentUpdate()` додається до компонента `OnlyEvens`. Зараз цей метод повертає `true`, тому `OnlyEvens` ререндерить щоразу, як він отримує нові `props`. Змініть метод так, щоб `OnlyEvens` оновлювався лише коли `value` нового props парна. Натисніть `Add` кнопку та перегляньте порядок дій у консолі браузера, в той час, як працює цикл.
# --hints--
Компонент `Controller` має показувати компонент `OnlyEvens` як дочірній елемент.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
return (
mockedComponent.find('Controller').length === 1 &&
mockedComponent.find('OnlyEvens').length === 1
);
})()
);
```
Метод `shouldComponentUpdate` має бути визначеним у компоненті `OnlyEvens`.
```js
assert(
(() => {
const child = React.createElement(OnlyEvens)
.type.prototype.shouldComponentUpdate.toString()
.replace(/s/g, '');
return child !== 'undefined';
})()
);
```
Компонент `OnlyEvens` має повертати тег `h1`, що рендерить значення `this.props.value`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
const first = () => {
mockedComponent.setState({ value: 1000 });
return mockedComponent.find('h1').html();
};
const second = () => {
mockedComponent.setState({ value: 10 });
return mockedComponent.find('h1').html();
};
const firstValue = first();
const secondValue = second();
assert(firstValue === '<h1>1000</h1>' && secondValue === '<h1>10</h1>');
})();
```
`OnlyEvens` має ререндерити лише тоді, коли `nextProps.value` парна.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
const first = () => {
mockedComponent.setState({ value: 8 });
return mockedComponent.find('h1').text();
};
const second = () => {
mockedComponent.setState({ value: 7 });
return mockedComponent.find('h1').text();
};
const third = () => {
mockedComponent.setState({ value: 42 });
return mockedComponent.find('h1').text();
};
const firstValue = first();
const secondValue = second();
const thirdValue = third();
assert(firstValue === '8' && secondValue === '8' && thirdValue === '42');
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Controller />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class OnlyEvens extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should I update?');
// Change code below this line
return true;
// Change code above this line
}
componentDidUpdate() {
console.log('Component re-rendered.');
}
render() {
return <h1>{this.props.value}</h1>;
}
}
class Controller extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
this.addValue = this.addValue.bind(this);
}
addValue() {
this.setState(state => ({
value: state.value + 1
}));
}
render() {
return (
<div>
<button onClick={this.addValue}>Add</button>
<OnlyEvens value={this.state.value} />
</div>
);
}
}
```
# --solutions--
```jsx
class OnlyEvens extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should I update?');
// Change code below this line
return nextProps.value % 2 === 0;
// Change code above this line
}
componentDidUpdate() {
console.log('Component re-rendered.');
}
render() {
return <h1>{this.props.value}</h1>;
}
}
class Controller extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
this.addValue = this.addValue.bind(this);
}
addValue() {
this.setState(state => ({
value: state.value + 1
}));
}
render() {
return (
<div>
<button onClick={this.addValue}>Add</button>
<OnlyEvens value={this.state.value} />
</div>
);
}
}
```

View File

@@ -0,0 +1,112 @@
---
id: 5a24c314108439a4d403616c
title: Заміна пропсів за замовчуванням
challengeType: 6
forumTopicId: 301399
dashedName: override-default-props
---
# --description--
Можливість встановлення пропсів за замовчуванням є корисною функцією в React. Спосіб заміни пропсів за замовчуванням полягає в тому, щоб безпосередньо встановити значення пропсу для компонента.
# --instructions--
Зараз компонент `ShoppingCart` відображає дочірній компонент `Items`. Цей компонент `Items` має пропс `quantity` за замовчуванням, встановлений на ціле число `0`. Замінити пропс за замовчуванням, передаючи значення `10` для `quantity`.
**Примітка:** Пам’ятайте, що синтаксис додавання пропсу до компонента схожий на додавання атрибутів HTML. Однак, оскільки значення `quantity` є цілим числом, його пишуть не в лапках, а ставлять в фігурні дужки. Наприклад, `{100}`. Цей синтаксис повідомляє JSX інтерпретувати значення в дужках безпосередньо як JavaScript.
# --hints--
Компонент `ShoppingCart` повинен відображатися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return mockedComponent.find('ShoppingCart').length === 1;
})()
);
```
Компонент `Items` повинен відображатися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return mockedComponent.find('Items').length === 1;
})()
);
```
Компонент `Items` повинен мати пропс `{ quantity: 10 }`, переданий з компонента `ShoppingCart`.
```js
(getUserInput) =>
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return (
mockedComponent.find('Items').props().quantity == 10 &&
getUserInput('index')
.replace(/ /g, '')
.includes('<Itemsquantity={10}/>')
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ShoppingCart />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const Items = (props) => {
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
}
Items.defaultProps = {
quantity: 0
}
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
{ /* Change code below this line */ }
return <Items />
{ /* Change code above this line */ }
}
};
```
# --solutions--
```jsx
const Items = (props) => {
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
}
Items.defaultProps = {
quantity: 0
}
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
{ /* Change code below this line */ }
return <Items quantity = {10} />
{ /* Change code above this line */ }
}
};
```

View File

@@ -0,0 +1,217 @@
---
id: 5a24c314108439a4d403617b
title: Передача зворотного виклику в якості пропсів
challengeType: 6
forumTopicId: 301400
dashedName: pass-a-callback-as-props
---
# --description--
Можна передавати `state` у якості пропсів дочірнім компонентам, але ви необмежені у передачі даних. Також можна передати функції обробника або будь-який метод, який визначений у компоненті React, дочірньому компоненту. Саме таким чином, дочірнім компонентам надають дозвіл на взаємодію з їхніми батьківськими компонентами. Відбувається передача методів дочірньому елементу, як звичайному пропсу. Він отримує назву, а ви - доступ до назви цього методу у розділі `this.props` у дочірньому компоненті.
# --instructions--
У редакторі коду виділено три компоненти. Компонент `MyApp` є батьківським, що відображає дочірні компоненти `GetInput` та `RenderInput`. Додайте компонент `GetInput` до методу візуалізації у `MyApp`, а потім надайте йому пропс з назвою `input`, призначений для `inputValue` із `MyApp`'s `state`. Також створіть пропс під назвою `handleChange` і передайте йому обробника введення даних `handleChange`.
Далі, додайте `RenderInput` до методу візуалізації в `MyApp`, після цього створіть пропс під назвою `input` і передайте йому `inputValue` із `state`. Після завершення з'являється можливість вводити інформацію у поле `input` у компоненті `GetInput`, який потім викликає метод обробника у своєму батьківському компоненті за допомогою пропсів. Це оновлює вхідні дані у `state` батьківського компонента, які передаються в якості пропсів обом дочірнім елементам. Зверніть увагу, як передаються дані між компонентами і як єдиним джерелом достовірних даних залишається `state` батьківського компонента. Слід визнати, що цей приклад не є повністю правдивим, але він повинен слугувати ілюстрацією того, як дані та зворотні виклики можуть передаватися між компонентами React.
# --hints--
Компонент `MyApp` повинен відображатися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return mockedComponent.find('MyApp').length === 1;
})()
);
```
Компонент `GetInput` повинен відображатися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return mockedComponent.find('GetInput').length === 1;
})()
);
```
Компонент `RenderInput` повинен відображатися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return mockedComponent.find('RenderInput').length === 1;
})()
);
```
Компонент `GetInput` повинен отримувати властивість стану `MyApp` `inputValue` у якості пропсів і містити елемент `input`, який змінює стан `MyApp`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const state_1 = () => {
mockedComponent.setState({ inputValue: '' });
return waitForIt(() => mockedComponent.state());
};
const state_2 = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: 'TestInput' } });
return waitForIt(() => mockedComponent.state());
};
const updated_1 = await state_1();
const updated_2 = await state_2();
assert(updated_1.inputValue === '' && updated_2.inputValue === 'TestInput');
};
```
Компонент `RenderInput` повинен отримувати властивість стану `MyApp` `inputValue` у якості пропсів.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const state_1 = () => {
mockedComponent.setState({ inputValue: 'TestName' });
return waitForIt(() => mockedComponent);
};
const updated_1 = await state_1();
assert(updated_1.find('p').text().includes('TestName'));
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyApp />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
inputValue: event.target.value
});
}
render() {
return (
<div>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
class GetInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Get Input:</h3>
<input
value={this.props.input}
onChange={this.props.handleChange}/>
</div>
);
}
};
class RenderInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Input Render:</h3>
<p>{this.props.input}</p>
</div>
);
}
};
```
# --solutions--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
inputValue: event.target.value
});
}
render() {
return (
<div>
<GetInput
input={this.state.inputValue}
handleChange={this.handleChange}/>
<RenderInput
input={this.state.inputValue}/>
</div>
);
}
};
class GetInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Get Input:</h3>
<input
value={this.props.input}
onChange={this.props.handleChange}/>
</div>
);
}
};
class RenderInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Input Render:</h3>
<p>{this.props.input}</p>
</div>
);
}
};
```

View File

@@ -0,0 +1,186 @@
---
id: 5a24c314108439a4d403616a
title: Передача масиву в якості пропсів
challengeType: 6
forumTopicId: 301401
dashedName: pass-an-array-as-props
---
# --description--
В останньому завданні було показано як передавати інформацію від батьківського компонента до дочірнього у якості `props` або властивостей. Це завдання демонструє як масиви можуть бути передані у якості `props`. Щоб передати масив елементу JSX, його слід обробляти як JavaScript і ставити у фігурні дужки.
```jsx
<ParentComponent>
<ChildComponent colors={["green", "blue", "red"]} />
</ParentComponent>
```
Потім дочірній компонент отримує доступ до властивості масиву `colors`. При доступі до властивості можна використовувати такі методи масиву, як `join()`. `const ChildComponent = (props) => <p>{props.colors.join(', ')}</p>` Це об’єднає всі елементи масиву `colors` у рядок, розділений комами і створить: `<p>green, blue, red</p>`. Пізніше дізнаємось про інші поширені методи візуалізації масивів даних у React.
# --instructions--
У редакторі коду є компоненти `List` та `ToDo`. Під час візуалізації кожного компоненту `List` з компоненту `ToDo`, передайте властивість `tasks`, призначену для масиву завдань до виконання, наприклад, `["walk dog", "workout"]`. Потім перейдіть до цього масиву `tasks` у компоненті `List`, показуючи його значення у межах елементу `p`. Використовуйте `join(", ")`, щоб відобразити масив `props.tasks` в елементі `p` як список, розділений комами. У сьогоднішньому списку має бути щонайменше 2 завдання, а у завтрашньому - щонайменше 3 завдання.
# --hints--
Компонент `ToDo` повинен відображати одинарний зовнішній `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.children().first().type() === 'div';
})()
);
```
Третій дочірній компонент `ToDo` повинен бути зразком компоненту `List`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.children().first().childAt(2).name() === 'List';
})()
);
```
П'ятий дочірній компонент `ToDo` повинен бути зразком компоненту `List`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.children().first().childAt(4).name() === 'List';
})()
);
```
Обидва зразки компонента `List` повинні мати властивість `tasks`, і `tasks` повинен бути масивом типу.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return (
Array.isArray(mockedComponent.find('List').get(0).props.tasks) &&
Array.isArray(mockedComponent.find('List').get(1).props.tasks)
);
})()
);
```
Перший компонент `List`, що представляє завдання на сьогодні, повинен містити 2 або більше елементів.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.find('List').get(0).props.tasks.length >= 2;
})()
);
```
Другий компонент `List`, що представляє завдання на завтра, повинен містити 3 або більше елементів.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return mockedComponent.find('List').get(1).props.tasks.length >= 3;
})()
);
```
Компонент `List` повинен відображати значення пропсу `tasks` у тегу `p`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
return (
mockedComponent
.find('p')
.get(0)
.props.children.replace(/\s*,\s*/g, ',') ===
mockedComponent
.find('List')
.get(0)
.props.tasks.join(',')
.replace(/\s*,\s*/g, ',') &&
mockedComponent
.find('p')
.get(1)
.props.children.replace(/\s*,\s*/g, ',') ===
mockedComponent
.find('List')
.get(1)
.props.tasks.join(',')
.replace(/\s*,\s*/g, ',')
);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ToDo />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const List = (props) => {
{ /* Change code below this line */ }
return <p>{}</p>
{ /* Change code above this line */ }
};
class ToDo extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>To Do Lists</h1>
<h2>Today</h2>
{ /* Change code below this line */ }
<List/>
<h2>Tomorrow</h2>
<List/>
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
const List= (props) => {
return <p>{props.tasks.join(', ')}</p>
};
class ToDo extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>To Do Lists</h1>
<h2>Today</h2>
<List tasks={['study', 'exercise']} />
<h2>Tomorrow</h2>
<List tasks={['call Sam', 'grocery shopping', 'order tickets']} />
</div>
);
}
};
```

View File

@@ -0,0 +1,164 @@
---
id: 5a24c314108439a4d4036169
title: Передача пропсів простому функціональному компоненту
challengeType: 6
forumTopicId: 301402
dashedName: pass-props-to-a-stateless-functional-component
---
# --description--
Попередні завдання охоплювали багато інформації стосовно створення і складання елементів JSX, функціональних компонентів та компонентів класу ES6 в React. Маючи таку базу, саме час перейти до іншої дуже поширеної функції у React: **props**. У React можна передавати пропси або властивості дочірнім компонентам. Припустимо, є компонент `App`, який відображає дочірній компонент з назвою `Welcome`, що є простим функціональним компонентом. Можна передати `Welcome` властивості `user` таким чином:
```jsx
<App>
<Welcome user='Mark' />
</App>
```
Використовуйте самостійно створений та який підтримується React **custom HTML attributes** для передачі компоненту. У цьому випадку, створена властивість `user` передається компоненту `Welcome`. Оскільки `Welcome` є простим функціональним компонентом, він має такий доступ до цього значення:
```jsx
const Welcome = (props) => <h1>Hello, {props.user}!</h1>
```
Як правило, це значення називають `props` і при роботі з простими функціональними компонентами, в основному воно розглядається як аргумент функції, яка відображає JSX. Можна отримати доступ до значення аргументу в тілі функції. З компонентами класу, все дещо інакше.
# --instructions--
У редакторі коду є компоненти `Calendar` та `CurrentDate`. При візуалізації `CurrentDate` з компоненту `Calendar`, передайте зазначену властивість `date` поточній даті об'єкту JavaScript `Date`. Потім перейдіть до `prop` у компоненті `CurrentDate`, демонструючи його значення у межах тегів `p`. Звернуть увагу, що для того, щоб значення `prop` оцінювалися як JavaScript, їх потрібно ставити у фігурні дужки, наприклад, `date={Date()}`.
# --hints--
Компонент `Calendar` повинен відображати одинарний елемент `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
return mockedComponent.children().type() === 'div';
})()
);
```
Другим дочірнім компонентом `Calendar` має бути компонент `CurrentDate`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
return mockedComponent.children().childAt(1).name() === 'CurrentDate';
})()
);
```
Компонент `CurrentDate` повинен мати пропс з назвою `date`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
return mockedComponent.children().childAt(1).props().date;
})()
);
```
Пропс `date` компонента `CurrentDate` повинен містити рядок тексту.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
const prop = mockedComponent.children().childAt(1).props().date;
return typeof prop === 'string' && prop.length > 0;
})()
);
```
Пропс `date` слід створити за допомогою виклику `Date()`
```js
assert(/<CurrentDatedate={Date\(\)}\/>/.test(__helpers.removeWhiteSpace(code)));
```
Компонент `CurrentDate` повинен відображати значення пропсу `date` у тегу `p`.
```js
let date = 'dummy date';
assert(
(function () {
const mockedComponent = Enzyme.mount(
React.createElement(CurrentDate, { date })
);
return mockedComponent.find('p').html().includes(date);
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Calendar />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const CurrentDate = (props) => {
return (
<div>
{ /* Change code below this line */ }
<p>The current date is: </p>
{ /* Change code above this line */ }
</div>
);
};
class Calendar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>What date is it?</h3>
{ /* Change code below this line */ }
<CurrentDate />
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
const CurrentDate = (props) => {
return (
<div>
{ /* Change code below this line */ }
<p>The current date is: {props.date}</p>
{ /* Change code above this line */ }
</div>
);
};
class Calendar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>What date is it?</h3>
{ /* Change code below this line */ }
<CurrentDate date={Date()} />
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,145 @@
---
id: 5a24c314108439a4d403617a
title: Передати стан як версію дочірніх компонентів
challengeType: 6
forumTopicId: 301403
dashedName: pass-state-as-props-to-child-components
---
# --description--
Ви бачили багато прикладів, які передавали версію дочірнім JSX елементам і дочірнім компонентів React в попередніх викликах. Вам, напевно, цікаво, звідки взявся цей пропс. Загальноприйнятою схемою є компонент стану, що містить `state` який важливо для вашого додатка, який потім відтворює дочірні компоненти. Ви хочете, щоб ці компоненти мали доступ до деяких частин цього `state`, який передано як props.
Наприклад, можливо, у вас є компонент `App`, який надає`Navbar`, серед інших компонентів. У вашому `App`, ви маєте `state` який містить багато інформації про користувачів, але для `Navbar` потрібен лише доступ до імені користувача, щоб він міг його відобразити. Ви передаєте цей фрагмент `state` в компонент `Navbar` як prop.
Ця модель ілюструє деякі важливі парадигми в React. Перший: *односторонній потік даних*. Стан рухається в одному напрямку вниз по дереву компонентів додатка, від вихідного компонента до дочірніх компонентів. Дочірні компоненти отримують тільки необхідні їм державні дані про стан. По-друге, складні програми стану можуть бути розбиті на декілька, а може бути, і на один компонент стану. Інші компоненти просто отримують стан від батьків в якості реквізиту і представляють для користувача інтерфейс з цього стану. Вона починає створювати поділ, коли управління державою обробляється в одній частині коду, а рендеринг призначеного для користувача інтерфейсу - в інший. Цей принцип відділення логіки стану від логіки призначеного для користувача інтерфейсу є одним з головних та важливих принципів React. Коли це використовується правильно, це робить дизайн складних, програм стану набагато простішим в управлінні.
# --instructions--
`MyApp` компонент має статус і відображає компоненту `Navbar` в якості підробки. Перетворювати `name` властивості в `state` вниз до дочірнього компонента, а потім показати `name/code> в <code>h1` тегу, що є частиною методу обробки `Navbar`. `name` повинно з'явитися після тексту `Привіт, моє ім'я:`.
# --hints--
Компонент`MyApp` повинен візуалізуватися і відобразити компонент `Navbar` в якості підробки.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
return (
mockedComponent.find('MyApp').length === 1 &&
mockedComponent.find('Navbar').length === 1
);
})()
);
```
`Navbar` компонент повинен отримувати властивість стану `MyApp` `name<` у якості props.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const setState = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.find('Navbar').props());
};
const navProps = await setState();
assert(navProps.name === 'TestName');
};
```
`h1` у `Navbar`Елемент має відобразити `name` prop.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
const navH1Before = mockedComponent.find('Navbar').find('h1').text();
const setState = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.find('Navbar').find('h1').text());
};
const navH1After = await setState();
assert(new RegExp('TestName').test(navH1After) && navH1After !== navH1Before);
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyApp />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'CamperBot'
}
}
render() {
return (
<div>
{/* Change code below this line */}
<Navbar />
{/* Change code above this line */}
</div>
);
}
};
class Navbar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{/* Change code below this line */}
<h1>Hello, my name is: </h1>
{/* Change code above this line */}
</div>
);
}
};
```
# --solutions--
```jsx
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'CamperBot'
}
}
render() {
return (
<div>
<Navbar name={this.state.name}/>
</div>
);
}
};
class Navbar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Hello, my name is: {this.props.name}</h1>
</div>
);
}
};
```

View File

@@ -0,0 +1,159 @@
---
id: 5a24c314108439a4d4036167
title: Відображення компонентів класу в DOM
challengeType: 6
forumTopicId: 301404
dashedName: render-a-class-component-to-the-dom
---
# --description--
Ви вже зустрічалися з використанням ReactDOM API для рендерингу JSX-елементів у DOM в попередніх завданнях. Процес рендерингу компонентів React буде схожим. Останні кілька завдань зосереджувалися на компонентах та композиції, тому рендеринг виконувався для вас за замовчуванням. Проте, жоден з написаного вами коду на React, не буде рендеритися в DOM без виклику ReactDOM API.
Ось оновлення синтаксису: `ReactDOM.render(componentToRender, targetNode)`. Першим аргументом є компонент React, який ви хочете відобразити. Другий аргумент - DOM-вузол, всередині якого ви хочете відобразити компонент.
Компоненти React передаються в `ReactDOM.render()` дещо інакше ніж елементи JSX. У JSX-елементах ви передаєте ім'я компонента, який хочете відобразити. Однак для компонентів React вам треба використовувати той самий синтаксис, що й під час рендерингу вкладених компонентів, наприклад `ReactDOM.render(<ComponentToRender />, targetNode)`. Такий синтаксис використовується як для класових, так і для функціональних компонентів ES6.
# --instructions--
Як компонент `Fruits`, так і компонент `Vegetables` визначені для вас за замовчуванням. Відобразіть обидва компоненти як дочірні для компонента `TypesOfFood`, далі відобразіть `TypesOfFood` у DOM. Також можна скористатися `div` з `id='challenge-node'`.
# --hints--
Компонент `TypesOfFood` повинен повертати одинарний елемент `div`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().type() === 'div';
})()
);
```
Компонент `TypesOfFood` повинен відображати компонент `Fruits` після елемента `h1`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(1).name() === 'Fruits';
})()
);
```
Компонент `TypesOfFood` повинен рендерити компонент `Vegetables` після компонента `Fruits`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
return mockedComponent.children().childAt(2).name() === 'Vegetables';
})()
);
```
Компонент `TypesOfFood` повинен відображатися у DOM всередині `div` з id `challenge-node`.
```js
assert(
(function () {
const html = document.getElementById('challenge-node').childNodes[0]
.innerHTML;
return (
html.includes(
'<div><h2>Fruits:</h2><h4>Non-Citrus:</h4><ul><li>Apples</li><li>Blueberries</li><li>Strawberries</li><li>Bananas</li></ul><h4>Citrus:</h4><ul><li>Lemon</li><li>Lime</li><li>Orange</li><li>Grapefruit</li></ul></div>'
) &&
html.includes(
'<div><h2>Vegetables:</h2><ul><li>Brussel Sprouts</li><li>Broccoli</li><li>Squash</li></ul></div>'
)
);
})()
);
```
# --seed--
## --before-user-code--
```jsx
const Fruits = () => {
return (
<div>
<h2>Fruits:</h2>
<h4>Non-Citrus:</h4>
<ul>
<li>Apples</li>
<li>Blueberries</li>
<li>Strawberries</li>
<li>Bananas</li>
</ul>
<h4>Citrus:</h4>
<ul>
<li>Lemon</li>
<li>Lime</li>
<li>Orange</li>
<li>Grapefruit</li>
</ul>
</div>
);
};
const Vegetables = () => {
return (
<div>
<h2>Vegetables:</h2>
<ul>
<li>Brussel Sprouts</li>
<li>Broccoli</li>
<li>Squash</li>
</ul>
</div>
);
};
```
## --seed-contents--
```jsx
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{/* Change code below this line */}
{/* Change code above this line */}
</div>
);
}
};
// Change code below this line
```
# --solutions--
```jsx
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{/* Change code below this line */}
<Fruits />
<Vegetables />
{/* Change code above this line */}
</div>
);
}
};
// Change code below this line
ReactDOM.render(<TypesOfFood />, document.getElementById('challenge-node'));
```

View File

@@ -0,0 +1,303 @@
---
id: 5a24c314108439a4d4036188
title: Умовний рендеринг з пропс
challengeType: 6
forumTopicId: 301405
dashedName: render-conditionally-from-props
---
# --description--
Поки ви бачили, як використовувати `if/else`, `&&` та трикомпонентний оператор (`condition ? expressionIfTrue : expressionIfFalse`) для прийняття умовних рішень про те, що треба рендерити та коли. Проте залишилася ще одна важлива для огляду тема, вивчення якої дозволить поєднувати всі ці поняття з іншою потужною функцією React: пропс. Використання пропсів для умовного рендерингу коду є дуже поширеним явищем серед React-розробників, таким чином, вони використовують значення даного пропсу для автоматичного прийняття рішень про те, що рендерити.
В цьому завданні вам доведеться налаштовувати дочірній компонент задля прийняття рішень на основі пропс стосовно рендерингу. Також ви зіштовхнетеся з використанням трикомпонентного оператора та побачите, як деякі з інших, описаних у минулих завданнях, понять можуть бути настільки ж корисними у даному контексті.
# --instructions--
Редактор коду складається з двох компонентів, які частково для вас визначені: батьківський, з назвою `GameOfChance`, та дочірній - `Results`. Вони використовуються при створенні простих ігор, де користувач натискає клавішу, аби перевірити, виграв він або вона чи ні.
Перш за все, вам знадобиться простий вираз, який випадковим чином повертає різні значення при кожному запуску. Можна використати `Math.random()`. Цей метод повертає значення між `0` (включно) та `1` (виключно) кожного разу, коли він викликається. Таким чином, для шансу 50/50 використовуйте у своєму виразі `Math.random() >= .5`. З точки зору статистики, цей вираз поверне `true` в 50% випадків та `false` - в інших 50%. В методі рендерингу, замініть `null` на вираз, наведений вище, задля завершення оголошення змінної.
Тепер у вас є вираз, здатний приймати випадкове рішення у коді. Наступне, що треба - це впровадити його. Відрендеріть компонент `Results` як дочірний від `GameOfChance` та передайте `expression` як пропс з назвою `fiftyFifty`. В компоненті `Results` напишіть трикомпонентний вираз на основі пропсу `fiftyFifty`, взятого з `GameOfChance`, для рендерингу елементу `h1` з текстом `You Win!` або `You Lose!`. Вкінці переконайтеся, що метод `handleClick()` правильно обчислює кожний хід, щоб користувач знав скільки разів він зіграв. Це також дозволить користувачеві дізнатися, що компонент дійсно оновився, коли він виграв або програв двічі поспіль.
# --hints--
Компонент `GameOfChance` має існувати та рендеритися на сторінці.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).find('GameOfChance').length,
1
);
```
`GameOfChance` має повертати окремий елемент `button`.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).find('button').length,
1
);
```
`GameOfChance` має повертати єдиний екземлпяр компоненту `Results`, який має пропс `fiftyFifty`.
```js
assert(
Enzyme.mount(React.createElement(GameOfChance)).find('Results').length ===
1 &&
Enzyme.mount(React.createElement(GameOfChance))
.find('Results')
.props()
.hasOwnProperty('fiftyFifty') === true
);
```
Стан `GameOfChance` має бути ініціалізовано властивістю `counter`, що має значення `1`.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).state().counter,
1
);
```
Якщо компонент `GameOfChance` вперше відображається в DOM, елемент `p` має повернутися із внутрішнім текстом `Turn: 1`.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(GameOfChance)).find('p').text(),
'Turn: 1'
);
```
Кожного разу, коли натискається кнопка, стан лічильника має збільшуватися на значення 1, а одинарний елемент `p` повинен бути відображений у DOM, що містить текст text `Turn: N`, де `N` - це значення стану лічильника.
```js
(() => {
const comp = Enzyme.mount(React.createElement(GameOfChance));
const simulate = () => {
comp.find('button').simulate('click');
};
const result = () => ({
count: comp.state('counter'),
text: comp.find('p').text()
});
const _1 = () => {
simulate();
return result();
};
const _2 = () => {
simulate();
return result();
};
const _3 = () => {
simulate();
return result();
};
const _4 = () => {
simulate();
return result();
};
const _5 = () => {
simulate();
return result();
};
const _1_val = _1();
const _2_val = _2();
const _3_val = _3();
const _4_val = _4();
const _5_val = _5();
assert(
_1_val.count === 2 &&
_1_val.text === 'Turn: 2' &&
_2_val.count === 3 &&
_2_val.text === 'Turn: 3' &&
_3_val.count === 4 &&
_3_val.text === 'Turn: 4' &&
_4_val.count === 5 &&
_4_val.text === 'Turn: 5' &&
_5_val.count === 6 &&
_5_val.text === 'Turn: 6'
);
})();
```
Коли компонент `GameOfChance` спочатку підключається до DOM, і далі, після кожного натискання кнопки, має повертатися одинарний елемент `h1`, який випадковим чином відображає або `You Win!`, або `You Lose!`.
```js
(() => {
const comp = Enzyme.mount(React.createElement(GameOfChance));
const simulate = () => {
comp.find('button').simulate('click');
};
const result = () => ({
h1: comp.find('h1').length,
text: comp.find('h1').text()
});
const _1 = result();
const _2 = () => {
simulate();
return result();
};
const _3 = () => {
simulate();
return result();
};
const _4 = () => {
simulate();
return result();
};
const _5 = () => {
simulate();
return result();
};
const _6 = () => {
simulate();
return result();
};
const _7 = () => {
simulate();
return result();
};
const _8 = () => {
simulate();
return result();
};
const _9 = () => {
simulate();
return result();
};
const _10 = () => {
simulate();
return result();
};
const _2_val = _2();
const _3_val = _3();
const _4_val = _4();
const _5_val = _5();
const _6_val = _6();
const _7_val = _7();
const _8_val = _8();
const _9_val = _9();
const _10_val = _10();
const __text = new Set([
_1.text,
_2_val.text,
_3_val.text,
_4_val.text,
_5_val.text,
_6_val.text,
_7_val.text,
_8_val.text,
_9_val.text,
_10_val.text
]);
const __h1 = new Set([
_1.h1,
_2_val.h1,
_3_val.h1,
_4_val.h1,
_5_val.h1,
_6_val.h1,
_7_val.h1,
_8_val.h1,
_9_val.h1,
_10_val.h1
]);
assert(__text.size === 2 && __h1.size === 1);
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<GameOfChance />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class Results extends React.Component {
constructor(props) {
super(props);
}
render() {
{/* Change code below this line */}
return <h1></h1>;
{/* Change code above this line */}
}
}
class GameOfChance extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => {
// Complete the return statement:
return {
counter: prevState
}
});
}
render() {
const expression = null; // Change this line
return (
<div>
<button onClick={this.handleClick}>Play Again</button>
{/* Change code below this line */}
{/* Change code above this line */}
<p>{'Turn: ' + this.state.counter}</p>
</div>
);
}
}
```
# --solutions--
```jsx
class Results extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h1>{this.props.fiftyFifty ? 'You Win!' : 'You Lose!'}</h1>;
}
}
class GameOfChance extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => {
return {
counter: prevState.counter + 1
}
});
}
render() {
const expression = Math.random() >= 0.5;
return (
<div>
<button onClick={this.handleClick}>Play Again</button>
<Results fiftyFifty={expression} />
<p>{'Turn: ' + this.state.counter}</p>
</div>
);
}
}
```

View File

@@ -0,0 +1,75 @@
---
id: 5a24bbe0dba28a8d3cbd4c5f
title: Візуалізація елементів HTML в DOM
challengeType: 6
forumTopicId: 301406
dashedName: render-html-elements-to-the-dom
---
# --description--
Отже, ви вже дізналися, що JSX є зручним інструментом, щоб писати читабельний HTML всередині JavaScript. За допомогою React, ми можемо візуалізувати JSX прямо до HTML DOM, використовуючи React's rendering API, відомий як ReactDOM.
ReactDOM пропонує простий метод, щоб візуалізувати React елементи в DOM, який виглядає так: `ReactDOM.render(componentToRender, targetNode)`, де першим аргументом є React елемент або компонент, який ви хочете візуалізувати, а другий аргумент - це DOM вузол, який ви хочете перетворити компонент.
Як ви і очікуєте, `ReactDOM.render()` має бути названий після оголошення елементів JSX, так само, як ви повинні створювати змінні перед тим, як їх використанням.
# --instructions--
Редактор коду має простий компонент JSX. Використовуйте метод `ReactDOM.render()`, для того щоб візуалізувати цей компонент на сторінку. Ви можете передавати визначені JSX елементи безпосередньо як перший аргумент і використовувати `document.getElementById()` для того, щоб вибрати DOM вузол для їх відображення. Також є `div` with `id='challenge-node'` який доступний вам для використання. Переконайтеся, що ви не змінили сталу `JSX`.
# --hints--
Стала `JSX` має повертати елемент `div`.
```js
assert(JSX.type === 'div');
```
`div` має містити тег `h1` як перший елемент.
```js
assert(JSX.props.children[0].type === 'h1');
```
`div` має містити тег `p` як другий елемент.
```js
assert(JSX.props.children[1].type === 'p');
```
Наданий елемент JSX має відображатися в DOM вузла за допомогою id `challenge-node`.
```js
assert(
document.getElementById('challenge-node').childNodes[0].innerHTML ===
'<h1>Hello World</h1><p>Lets render this to the DOM</p>'
);
```
# --seed--
## --seed-contents--
```jsx
const JSX = (
<div>
<h1>Hello World</h1>
<p>Lets render this to the DOM</p>
</div>
);
// Change code below this line
```
# --solutions--
```jsx
const JSX = (
<div>
<h1>Hello World</h1>
<p>Lets render this to the DOM</p>
</div>
);
// Change code below this line
ReactDOM.render(JSX, document.getElementById('challenge-node'));
```

View File

@@ -0,0 +1,76 @@
---
id: 5a24c314108439a4d403618d
title: Візуалізація React на сервері з renderToString
challengeType: 6
forumTopicId: 301407
dashedName: render-react-on-the-server-with-rendertostring
---
# --description--
Отже, ви вже візуалізували компоненти React для клієнтів. Звичайно, це є те, що ви будете робити завжди. Однак, є деякі випадки, де є сенс в тому, щоб візуалізувати компонент React на сервері. Це є можливим, оскільки React є бібліотекою для перегляду JavaScript і ви можете запустити JavaScript на сервері з Node. Дійсно, React надає метод `renderToString()`, який ви можете використовувати для цієї мети.
Існує дві ключові причини, чому візуалізація на сервері може використовуватися в додатках, якими користується весь світ. По-перше, якщо цього не робити, то ваші React додатки будуть складатися з відносно порожнього файлу HTML і великого набору JavaScript, коли він початково завантажений у браузер. Це може бути не ідеальним для пошукової системи, яка намагається індексувати вміст ваших сторінок, тож люди зможуть вас знайти. Якщо ви візуалізуєте початкову HTML розмітку на сервері та надішлете це клієнту, то початкове завантаження сторінки буде містити усі розмітки сторінки, які можуть бути переглянуті пошуковими системами. По-друге, це створює досвід швидшого початкового завантаження сторінки, тому що візуалізований HTML є меншим, ніж JavaScript код всього додатку. React все ще зможе розпізнати ваш додаток і керувати ним після початкового завантаження.
# --instructions--
Метод `renderToString()` надається на `ReactDOMServer`, який є доступним, як глобальний об'єкт. Метод приймає один аргумент, який є елементом React. Використовуйте це для того, щоб візуалізувати `App` в рядку.
# --hints--
Компонент `App` має візуалізувати рядок використовуючи `ReactDOMServer.renderToString`.
```js
(getUserInput) =>
assert(
getUserInput('index')
.replace(/ /g, '')
.includes('ReactDOMServer.renderToString(<App/>)') &&
Enzyme.mount(React.createElement(App)).children().name() === 'div'
);
```
# --seed--
## --before-user-code--
```jsx
var ReactDOMServer = { renderToString(x) { return null; } };
```
## --after-user-code--
```jsx
ReactDOM.render(<App />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div/>
}
};
// Change code below this line
```
# --solutions--
```jsx
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div/>
}
};
// Change code below this line
ReactDOMServer.renderToString(<App/>);
```

View File

@@ -0,0 +1,119 @@
---
id: 5a24c314108439a4d4036172
title: Інший спосіб відображення статусу в інтерфейсі користувача
challengeType: 6
forumTopicId: 301408
dashedName: render-state-in-the-user-interface-another-way
---
# --description--
Існує інший спосіб доступу до `state` в компоненті. У методі `render()` можна безпосередньо писати JavaScript перед командою `return`. Наприклад, можна задавати функції, отримувати доступ до даних з `state` чи `props`, виконувати обчислення за цими даними тощо. Потім можна призначити будь-які дані змінним, до яких у вас є доступ в команді `return`.
# --instructions--
У методі візуалізації `MyComponent` визначте `const` під іменем `name` та встановіть значення рівне значенню імені в компоненті `state`. Оскільки можна написати JavaScript безпосередньо в цій частині коду, вам не потрібно ставити це посилання у фігурні дужки.
Далі в операторі повернення відобразіть це значення в теґу `h1`, використовуючи змінну `name`. Пам’ятайте, що в операторі повернення необхідно використовувати синтаксис JSX (фігурні дужки для JavaScript).
# --hints--
`MyComponent` має мати ключі доступу `name` зі значенням `freeCodeCamp`, збереженим у його стані.
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
'freeCodeCamp'
);
```
`MyComponent` повинен показувати заголовок `h1`, розміщений в одному `div`.
```js
assert(
/<div><h1>.*<\/h1><\/div>/.test(
Enzyme.mount(React.createElement(MyComponent)).html()
)
);
```
Відтворений теґ `h1` має містити посилання на `{name}`.
```js
(getUserInput) =>
assert(/<h1>\n*\s*\{\s*name\s*\}\s*\n*<\/h1>/.test(getUserInput('index')));
```
У заголовку `h1` має бути текст, переданий у стані компонента.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.html());
};
const firstValue = await first();
assert(firstValue === '<div><h1>TestName</h1></div>');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
// Change code below this line
// Change code above this line
return (
<div>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
// Change code below this line
const name = this.state.name;
// Change code above this line
return (
<div>
{ /* Change code below this line */ }
<h1>{name}</h1>
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,113 @@
---
id: 5a24c314108439a4d4036171
title: Статус відображення в інтерфейсі користувача
challengeType: 6
forumTopicId: 301409
dashedName: render-state-in-the-user-interface
---
# --description--
Визначивши початковий стан компонента, можна відобразити будь-яку його частину у візуалізованому інтерфейсі. Якщо компонент збережено, він завжди матиме доступ до даних у `state` в методі `render()`. Можна дістатися до даних за допомогою `this.state`.
Якщо ви хочете дістатися значення стану в межах `return` метода виконання, ви маєте взяти значення в фігурні дужки.
`state`- це одна з найпотужніших особливостей компонентів у React. Дозволяє відстежувати важливі дані у вашому додатку і відобразити інтерфейс в результаті змін у них. Якщо змінюються дані, зміниться і інтерфейс. React користується таким собі віртуальним DOM- ом щоб відстежувати зміни за лаштунками. При оновленні даних стану програма запускає повторне відображення компонентів, що використовують ці дані, включно з дочірніми компонентами, що отримали дані як підтримку. React оновлює DOM, але тільки за необхідності. Це означає, що не треба перейматися через зміну DOM. Ви просто вказуєте як має виглядати інтерфейс.
Зауважте, що якщо ви зберігаєте елемент, жоден інший компонент не знатиме його `state`. Його `state` буде повністю інкапсульованим, або локальним до того компонента доти, доки ви не передасте збереження дочірньому компоненту як `props`. Цей клас інкапсульованого `state` дуже важливий, бо дозволяє прописати певну логіку, а потім вмістити і ізолювати в одній з частин вашого коду.
# --instructions--
В редакторі коду `MyComponent` вже збережено. Визначення теґу `h1` в методі відображення компонента, що передає значення `name` стану компонента.
**Note:** `h1` має відображати тільки значення з `state`, і більш нічого. В JSX будь- який код у фігурних дужках `{ }` буде розглядатися як JavaScript. Тож для доступу до значення `state`, просто візьміть посилання в фігурні дужки.
# --hints--
`MyComponent` повинен мати ключ `name` зі збереженим у його стані значенням `freeCodeCamp`.
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
'freeCodeCamp'
);
```
`MyComponent` повинен показувати заголовок `h1`, розміщений в одному `div`.
```js
assert(
/<div><h1>.*<\/h1><\/div>/.test(
Enzyme.mount(React.createElement(MyComponent)).html()
)
);
```
У заголовку `h1` має бути тільки текст, переданий у стані компонента.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.html());
};
const firstValue = await first();
const getValue = firstValue.replace(/\s/g, '');
assert(getValue === '<div><h1>TestName</h1></div>');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
return (
<div>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
return (
<div>
{ /* Change code below this line */ }
<h1>{this.state.name}</h1>
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,155 @@
---
id: 5a24c314108439a4d4036184
title: Візуалізація за допомогою умови If-Else
challengeType: 6
forumTopicId: 301410
dashedName: render-with-an-if-else-condition
---
# --description--
Інший додаток для використовування JavaScript, щоб контролювати наданий вами вид, полягає у тому, щоб зв'язати елементи, які візуалізуються в умові. Коли умова є правдивою, один вид візуалізується. Коли він є хибним, то це вже є інший вид. Ви можете робити це із стандартним реченням `if/else` в методі `render()` React компонента.
# --instructions--
MyComponent містить `boolean` у стані, який відстежує, чи хочете ви відтворити деякі елементи в UI, чи ні. `button` перемикає стан цього значення. На даний момент, він візуалізує той самий UI кожного разу. Якщо переписати метод `render()` із реченням `if/else`, і тоді `display` є `true`, то ви повернете поточну розмітку. В іншому випадку, це поверне розмітку без елемента `h1`.
**Note:** Ви маєте написати `if/else`, щоб пройти тести. Використання потрійного оператора в даному випадку не проходить.
# --hints--
`MyComponent` має існувати і візуалізуватися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('MyComponent').length === 1;
})()
);
```
Коли `display` встановити на `true`, то має візуалізуватися `div`, `button`, `h1`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: true });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
mockedComponent.find('div').length === 1 &&
mockedComponent.find('div').children().length === 2 &&
mockedComponent.find('button').length === 1 &&
mockedComponent.find('h1').length === 1
);
};
```
Коли `display` встановити на `false`, то має візуалізуватися тільки `div` і `button`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: false });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
mockedComponent.find('div').length === 1 &&
mockedComponent.find('div').children().length === 1 &&
mockedComponent.find('button').length === 1 &&
mockedComponent.find('h1').length === 0
);
};
```
Метод візуалізації має використовувати речення `if/else` для того, щоб перевірити умови `this.state.display`.
```js
(getUserInput) =>
assert(
getUserInput('index').includes('if') &&
getUserInput('index').includes('else')
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState((state) => ({
display: !state.display
}));
}
render() {
// Change code below this line
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
<h1>Displayed!</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState((state) => ({
display: !state.display
}));
}
render() {
// Change code below this line
if (this.state.display) {
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
<h1>Displayed!</h1>
</div>
);
} else {
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
</div>
);
}
}
};
```

View File

@@ -0,0 +1,145 @@
---
id: 5a24c314108439a4d403616f
title: Перегляд використання пропсів з простими функціональними компонентами
challengeType: 6
forumTopicId: 301411
dashedName: review-using-props-with-stateless-functional-components
---
# --description--
За винятком останнього завдання, ви передавали пропси до функціональних безструктурних компонентів. Ці компоненти діють як чисті функції. Вони отримують пропси як вхідні дані і повертають той самий вид кожен раз, коли вони передають одні і ті ж пропси. Вам може бути цікаво, що це за стан, наступне завдання розгляне це питання детальніше. Перед цим розглянемо термінологію компонентів.
*Функціональний безструктурний компонент * це будь-яка написана вами функція, яка приймає пропси та повертає JSX. *безструктурний компонент*, з іншого боку, це клас, який розширює `React.Component`, але не використовує внутрішній стан (буде розглянуто в наступному завданні). Зрештою, *структурний компонент* це компонент класу, який підтримує власний внутрішній стан. Компоненти зі збереженням стану можуть називатися просто компонентами або компонентами React.
Загальна схема полягає в спробі мінімізувати збереження стану та створити там, де це можливо, функціональні компоненти без збереження стану. Це допомагає обмежити управління вашим станом у певній сфері вашої програми. У свою чергу, це покращує розробку і супровід вашої програми, полегшуючи відстеження впливу зміни стану на його поведінку.
# --instructions--
Редактор коду має компонент `CampSite`, який відображає компонент `Camper` як дочірній. Визначте компонент `Camper` та призначте пропси за замовчуванням `{ name: 'CamperBot' }`. Всередині компоненту `Camper` відобразіть будь-який код, який хочете, але переконайтесь, що є хоча б один елемент `p`, який містить значення `name`, що передається як `prop`. Зрештою, визначте `propTypes` у компоненті `Camper`, щоб `name` був наданий як пропс та перевірте, що він категорії `string`.
# --hints--
Компонент `CampSite` повинен відображатися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
return mockedComponent.find('CampSite').length === 1;
})()
);
```
Компонент `Camper` повинен відображатися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
return mockedComponent.find('Camper').length === 1;
})()
);
```
Компонент `Camper` має містити пропси за замовчуванням, які призначають рядок `CamperBot` до ключа доступу `name`.
```js
assert(
/Camper.defaultProps={name:(['"`])CamperBot\1,?}/.test(
__helpers.removeWhiteSpace(code)
)
);
```
Компонент `Camper` має містити типи пропсів, які потребують, щоб пропс `name` був типу `string`.
```js
assert(
/Camper.propTypes={name:PropTypes.string.isRequired,?}/.test(
__helpers.removeWhiteSpace(code)
)
);
```
Компонент `Camper` має містити елемент `p` з лише текстом, взятим з пропсу `name`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
return (
mockedComponent.find('p').text() ===
mockedComponent.find('Camper').props().name
);
})()
);
```
# --seed--
## --before-user-code--
```jsx
var PropTypes = {
string: { isRequired: true }
};
```
## --after-user-code--
```jsx
ReactDOM.render(<CampSite />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class CampSite extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Camper/>
</div>
);
}
};
// Change code below this line
```
# --solutions--
```jsx
class CampSite extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Camper/>
</div>
);
}
};
// Change code below this line
const Camper = (props) => {
return (
<div>
<p>{props.name}</p>
</div>
);
};
Camper.propTypes = {
name: PropTypes.string.isRequired
};
Camper.defaultProps = {
name: 'CamperBot'
};
```

View File

@@ -0,0 +1,143 @@
---
id: 5a24c314108439a4d4036173
title: Встановлення State за допомогою this.setState
challengeType: 6
forumTopicId: 301412
dashedName: set-state-with-this-setstate
---
# --description--
В попередніх завданнях було розглянуто компонент `state` та як ініціалізувати стан в `constructor`. Також існує спосіб змінити стан компонента `state`. React надає метод для оновлення компоненту `state` під назвою `setState`. Ви викликаєте метод `setState` у своєму класі компонентів наступним чином: `this.setState()`, передаючи об'єкт з парами ключ-значення. Ключі - це властивості стану, а значення - оновлені дані стану. Наприклад, якби ми зберігали `username` в стані та хотіли б його оновити, то це мало б наступний вигляд:
```jsx
this.setState({
username: 'Lewis'
});
```
React очікує, що ви ніколи не будете змінювати `state` безпосередньо, натомість завжди будете використовувати `this.setState()`, коли відбуваються зміни стану. Також зверніть увагу на те, що React може пакетно оновлювати кілька станів, щоб покращити продуктивність. Це означає, що оновлення стану за допомогою методу `setState` можуть бути асинхронними. Існує альтернативний синтаксис для методу `setState`, який дозволяє обійти цю проблему. Він використовується рідко, але добре мати його на увазі! Для отримання додаткової інформації зверніться до [React документації](https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous).
# --instructions--
В редакторі коду є елемент `button`, який має обробник `onClick()`. Цей обробник спрацьовує, коли `button` отримує подію кліку в браузері та запускає метод `handleClick`, визначений в `MyComponent`. В межах методу `handleClick` оновіть компонент `state`, використовуючи `this.setState()`. Встановіть властивість `name` у `state`, щоб вона дорівнювала рядку `React Rocks!`.
Натисніть на кнопку та перегляньте оновлення візуалізованого стану. Не хвилюйтесь, якщо на цьому етапі ви не до кінця розумієте, як працює код обробника кліку. Це буде розглянуто в наступних завданнях.
# --hints--
Стан `MyComponent` має ініціалізуватися за допомогою пари ключ-значення `{ name: Initial State }`.
```js
assert(
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
'Initial State'
);
```
`MyComponent` має виводити елемент заголовку `h1`.
```js
assert(Enzyme.mount(React.createElement(MyComponent)).find('h1').length === 1);
```
У заголовку `h1` має бути текст, переданий у стані компонента.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'TestName' });
return waitForIt(() => mockedComponent.html());
};
const firstValue = await first();
assert(/<h1>TestName<\/h1>/.test(firstValue));
};
```
Виклик методу `handleClick` в `MyComponent` має встановити властивість назви в стані, що дорівнює `React Rocks!`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ name: 'Before' });
return waitForIt(() => mockedComponent.state('name'));
};
const second = () => {
mockedComponent.instance().handleClick();
return waitForIt(() => mockedComponent.state('name'));
};
const firstValue = await first();
const secondValue = await second();
assert(firstValue === 'Before' && secondValue === 'React Rocks!');
};
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Initial State'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// Change code below this line
// Change code above this line
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click Me</button>
<h1>{this.state.name}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Initial State'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// Change code below this line
this.setState({
name: 'React Rocks!'
});
// Change code above this line
}
render() {
return (
<div>
<button onClick = {this.handleClick}>Click Me</button>
<h1>{this.state.name}</h1>
</div>
);
}
};
```

View File

@@ -0,0 +1,146 @@
---
id: 5a24c314108439a4d4036185
title: Використання && для більш стислого умовного значення
challengeType: 6
forumTopicId: 301413
dashedName: use--for-a-more-concise-conditional
---
# --description--
В останньому завданні спрацювали оператори `if/else`, але є більш стислий спосіб досягти того ж самого результату. Уявіть, що ви відстежуєте кілька умов в компоненті та хочете, щоб різні елементи відображались залежно від кожної з цих умов. Якщо ви створюєте багато операторів `else if`, щоб повернути дещо інші інтерфейси користувачів, то в такому випадку ви можете повторювати код, а це залишає можливість помилки. Замість цього, ви можете використовувати логічний оператор `&&` для виконання умовної логіки в більш стислому вигляді. Це можливо, тому що ви хочете перевірити, чи є умова `true`, і якщо так, то повернути деяку розмітку. Ось приклад:
```jsx
{condition && <p>markup</p>}
```
Якщо `condition` встановлено `true`, тоді повертається розмітка. Якщо умова встановлена `false`, то операція одразу поверне `false` після оцінки `condition`, і тоді нічого не повертається. Ви можете включати ці оператори безпосередньо в ваш JSX та об'єднувати кілька умов, записуючи `&&` після кожної з них. Це дозволяє обробляти більш складну умовну логіку в вашому методі `render()` без повторення великої кількості коду.
# --instructions--
Розв'яжіть попередній приклад ще раз, так щоб `h1` відображався тільки, якщо `display` встановлено `true`, але використовуйте логічний оператор `&&` замість оператора `if/else`.
# --hints--
`MyComponent` має існувати та відображатись.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('MyComponent').length;
})()
);
```
Коли `display` встановлено на `true`, то мають виводитись `div`, `button`, та `h1`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: true });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
updated.find('div').length === 1 &&
updated.find('div').children().length === 2 &&
updated.find('button').length === 1 &&
updated.find('h1').length === 1
);
};
```
Коли `display` встановлено на `false`, то мають виводитись тільки `div` та `button`.
```js
async () => {
const waitForIt = (fn) =>
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const state_1 = () => {
mockedComponent.setState({ display: false });
return waitForIt(() => mockedComponent);
};
const updated = await state_1();
assert(
updated.find('div').length === 1 &&
updated.find('div').children().length === 1 &&
updated.find('button').length === 1 &&
updated.find('h1').length === 0
);
};
```
Метод візуалізації має використовувати логічний оператор `&&` для перевірки умови `this.state.display`.
```js
(getUserInput) => assert(getUserInput('index').includes('&&'));
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState(state => ({
display: !state.display
}));
}
render() {
// Change code below this line
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
<h1>Displayed!</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
display: true
}
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState(state => ({
display: !state.display
}));
}
render() {
// Change code below this line
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
{this.state.display && <h1>Displayed!</h1>}
</div>
);
}
};
```

View File

@@ -0,0 +1,280 @@
---
id: 5a24c314108439a4d4036187
title: Використовуйте трикомпонентний вираз для умовної візуалізації
challengeType: 6
forumTopicId: 301414
dashedName: use-a-ternary-expression-for-conditional-rendering
---
# --description--
Перед переходом до техніки динамічного малювання, існує останній спосіб використання вбудованих умов JavaScript для відображення вашого бажання: <dfn>Зовнішній оператор</dfn>. Зовнішній оператор часто використовується як ярлик для тверджень `if/else` у JavaScript. Вони не такі надійні, як традиційні `if/else`, але вони дуже популярні серед розробників React. Причиною цього є те, що компілювання JSX операційної системи `if/else` операторів не можна вставляти безпосередньо в код JSX. Можливо, ви помітили це раніше — коли потрібен оператор `if/else`, він завжди був *поза* ключовим словом `return`. Трикомпонентні вирази можуть бути чудовою альтернативою, якщо ви хочете реалізувати умовну логіку в межах вашої JSX. Нагадаємо, що трикомпонентний оператор має три частини, але ви можете поєднати декілька трикомпонентних вирази разом. Ось тут основний синтаксис:
```jsx
condition ? expressionIfTrue : expressionIfFalse;
```
# --instructions--
Редактор коду має три константи, визначені в межах методу `CheckUserAge` component's `render()`. Вони називаються `buttonOne`, `buttonTwo`, і `buttonThree`. Кожному із них призначено простий вираз JSX, що представляє елемент кнопки. Спочатку ініціалізувати стан `CheckUserAge` з `input` і `userAge`, обидва задані значенням пустого рядка.
Як тільки компонент відображає інформацію на сторінці, користувачі повинні мати спосіб взаємодіяти з нею. У межах `return` компонента, встановіть трикомпонентний вираз, який реалізує наступну логіку: коли сторінка вперше завантажується, відобразити кнопку підтвердження, `buttonOne` на сторінці. Потім, коли користувач введе свій вік та натисне кнопку, відобразіть іншу кнопку з урахуванням віку. Якщо користувач вводить число менше за `18`, відобразіть `buttonThree`. Якщо користувач вводить число більше або рівне `18`, відобразіть `buttonTwo`.
# --hints--
Компонент `CheckUserAge` має візуалізувати один елемент `input` і один елемент `button`.
```js
assert(
Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('input')
.length === 1 &&
Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('button')
.length === 1
);
```
Стан компонента `CheckUserAge` слід ініціалізувати з властивістю `userAge` і властивістю `input`, котрі обидва встановлюють значення пустого рядка.
```js
assert(
Enzyme.mount(React.createElement(CheckUserAge)).state().input === '' &&
Enzyme.mount(React.createElement(CheckUserAge)).state().userAge === ''
);
```
Коли компонент `CheckUserAge` першим візуалізовано в DOM, то текст `button` має бути Submit.
```js
assert(
Enzyme.mount(React.createElement(CheckUserAge)).find('button').text() ===
'Submit'
);
```
Коли вводиться число менше 18 в `input` і натискається `button`, внутрішній текст `button` має бути `You Shall Not Pass`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
const initialButton = mockedComponent.find('button').text();
const enter3AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '3' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const enter17AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '17' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const userAge3 = enter3AndClickButton();
const userAge17 = enter17AndClickButton();
assert(
initialButton === 'Submit' &&
userAge3 === 'You Shall Not Pass' &&
userAge17 === 'You Shall Not Pass'
);
})();
```
Коли вводиться число більше або рівне 18-и в `input` і натискається `button`, внутрішній текст `button` має бути `You May Enter`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
const initialButton = mockedComponent.find('button').text();
const enter18AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '18' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const enter35AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '35' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const userAge18 = enter18AndClickButton();
const userAge35 = enter35AndClickButton();
assert(
initialButton === 'Submit' &&
userAge18 === 'You May Enter' &&
userAge35 === 'You May Enter'
);
})();
```
Після того, як число було прийнято, і значення `input` знову змінилося, текст `button` повинен повернутися до `Submit`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
const enter18AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '18' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const changeInputDontClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '5' } });
mockedComponent.update();
return mockedComponent.find('button').text();
};
const enter10AndClickButton = () => {
mockedComponent
.find('input')
.simulate('change', { target: { value: '10' } });
mockedComponent.find('button').simulate('click');
mockedComponent.update();
return mockedComponent.find('button').text();
};
const userAge18 = enter18AndClickButton();
const changeInput1 = changeInputDontClickButton();
const userAge10 = enter10AndClickButton();
const changeInput2 = changeInputDontClickButton();
assert(
userAge18 === 'You May Enter' &&
changeInput1 === 'Submit' &&
userAge10 === 'You Shall Not Pass' &&
changeInput2 === 'Submit'
);
})();
```
Ваш код не повинен містити конструкцій `if/else`.
```js
assert(
new RegExp(/(\s|;)if(\s|\()/).test(
Enzyme.mount(React.createElement(CheckUserAge)).instance().render.toString()
) === false
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<CheckUserAge />, document.getElementById('root'));
```
## --seed-contents--
```jsx
const inputStyle = {
width: 235,
margin: 5
};
class CheckUserAge extends React.Component {
constructor(props) {
super(props);
// Change code below this line
// Change code above this line
this.submit = this.submit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
input: e.target.value,
userAge: ''
});
}
submit() {
this.setState(state => ({
userAge: state.input
}));
}
render() {
const buttonOne = <button onClick={this.submit}>Submit</button>;
const buttonTwo = <button>You May Enter</button>;
const buttonThree = <button>You Shall Not Pass</button>;
return (
<div>
<h3>Enter Your Age to Continue</h3>
<input
style={inputStyle}
type='number'
value={this.state.input}
onChange={this.handleChange}
/>
<br />
{/* Change code below this line */}
{/* Change code above this line */}
</div>
);
}
}
```
# --solutions--
```jsx
const inputStyle = {
width: 235,
margin: 5
};
class CheckUserAge extends React.Component {
constructor(props) {
super(props);
this.state = {
userAge: '',
input: ''
};
this.submit = this.submit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
input: e.target.value,
userAge: ''
});
}
submit() {
this.setState(state => ({
userAge: state.input
}));
}
render() {
const buttonOne = <button onClick={this.submit}>Submit</button>;
const buttonTwo = <button>You May Enter</button>;
const buttonThree = <button>You Shall Not Pass</button>;
return (
<div>
<h3>Enter Your Age to Continue</h3>
<input
style={inputStyle}
type='number'
value={this.state.input}
onChange={this.handleChange}
/>
<br />
{this.state.userAge === ''
? buttonOne
: this.state.userAge >= 18
? buttonTwo
: buttonThree}
</div>
);
}
}
```

View File

@@ -0,0 +1,334 @@
---
id: 5a24c314108439a4d4036183
title: Використовуйте розширений JavaScript у методі React Render
challengeType: 6
forumTopicId: 301415
dashedName: use-advanced-javascript-in-react-render-method
---
# --description--
Під час попередніх занять, ви вивчили, як вводити код JavaScript у код JSX, за допомогою фігурних дужок, `{ }`, для таких завдань, як отримання доступу до ресурсів, передачі ресурсів, перегляду поточного стану, додавання коментарів до вашого коду, і зовсім нещодавно для стилізації ваших компонентів. Усі вони є поширеними варіантами використання JavaScript у JSX, проте це не єдиний спосіб використання коду JavaScript у ваших компонентах React.
Ви також можете прямо вписати ваші методи JavaScript у `render`, перед командою `return`,, *** без ***, вставляючи їх у фігурні дужки. Це тому, що це ще не у межах коду JSX. Якщо ви захочете використати змінну пізніше у коді JSX * всередині команди * the `return`, потрібно помістити назву змінної у фігурні дужки.
# --instructions--
У даному коді, метод `render` складається з бази, що містить 20 аспектів, для представлення відповідей, знайдених у класичній іграшці 1980's Magic Eight Ball. Процес натискання кнопки пов'язаний з методом `ask`, тому при кожному натисканні на кнопку, відбудеться генерування випадкового номеру та його збереження у стані як `randomIndex`. На рядку 52, видаліть рядок `change me!` та перепризначте the `answer` const, аби ваш код випадковим чином організовував інший індекс з масиву `possibleAnswers` кожного разу, коли коли компонент оновлюється. Наостанок, вставте константу `answer` всередину `p` теґів.
# --hints--
Компонент `MagicEightBall` повинен існувати та відображатися на сторінці.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(MagicEightBall)).find('MagicEightBall')
.length,
1
);
```
Перший дочірній елемент `MagicEightBall` повинен бути елементом `input`.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(MagicEightBall))
.children()
.childAt(0)
.name(),
'input'
);
```
Третій дочірній елемент `MagicEightBall` повинен бути елементом `button`.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(MagicEightBall))
.children()
.childAt(2)
.name(),
'button'
);
```
Стан `MagicEightBall` повинен починатися з властивостями `userInput` та `randomIndex`, які обидва відповідають значенню пустого рядка.
```js
assert(
Enzyme.mount(React.createElement(MagicEightBall)).state('randomIndex') ===
'' &&
Enzyme.mount(React.createElement(MagicEightBall)).state('userInput') === ''
);
```
Коли `MagicEightBall` вперше піднімається до DOM, він повинен повернутися до пустого елементу `p`.
```js
assert(
Enzyme.mount(React.createElement(MagicEightBall)).find('p').length === 1 &&
Enzyme.mount(React.createElement(MagicEightBall)).find('p').text() === ''
);
```
Коли текст введено у елемент `input` та кнопку натиснуто, компонент `MagicEightBall` має повернутися до елементу a `p`, що містить випадковий елемент із масиву `possibleAnswers`.
```js
(() => {
const comp = Enzyme.mount(React.createElement(MagicEightBall));
const simulate = () => {
comp.find('input').simulate('change', { target: { value: 'test?' } });
comp.find('button').simulate('click');
};
const result = () => comp.find('p').text();
const _1 = () => {
simulate();
return result();
};
const _2 = () => {
simulate();
return result();
};
const _3 = () => {
simulate();
return result();
};
const _4 = () => {
simulate();
return result();
};
const _5 = () => {
simulate();
return result();
};
const _6 = () => {
simulate();
return result();
};
const _7 = () => {
simulate();
return result();
};
const _8 = () => {
simulate();
return result();
};
const _9 = () => {
simulate();
return result();
};
const _10 = () => {
simulate();
return result();
};
const _1_val = _1();
const _2_val = _2();
const _3_val = _3();
const _4_val = _4();
const _5_val = _5();
const _6_val = _6();
const _7_val = _7();
const _8_val = _8();
const _9_val = _9();
const _10_val = _10();
const actualAnswers = [
_1_val,
_2_val,
_3_val,
_4_val,
_5_val,
_6_val,
_7_val,
_8_val,
_9_val,
_10_val
];
const hasIndex = actualAnswers.filter(
(answer, i) => possibleAnswers.indexOf(answer) !== -1
);
const notAllEqual = new Set(actualAnswers);
assert(notAllEqual.size > 1 && hasIndex.length === 10);
})();
```
# --seed--
## --after-user-code--
```jsx
var possibleAnswers = [
'It is certain',
'It is decidedly so',
'Without a doubt',
'Yes, definitely',
'You may rely on it',
'As I see it, yes',
'Outlook good',
'Yes',
'Signs point to yes',
'Reply hazy try again',
'Ask again later',
'Better not tell you now',
'Cannot predict now',
'Concentrate and ask again',
"Don't count on it",
'My reply is no',
'My sources say no',
'Outlook not so good',
'Very doubtful',
'Most likely'
];
ReactDOM.render(<MagicEightBall />, document.getElementById('root'));
```
## --seed-contents--
```jsx
const inputStyle = {
width: 235,
margin: 5
};
class MagicEightBall extends React.Component {
constructor(props) {
super(props);
this.state = {
userInput: '',
randomIndex: ''
};
this.ask = this.ask.bind(this);
this.handleChange = this.handleChange.bind(this);
}
ask() {
if (this.state.userInput) {
this.setState({
randomIndex: Math.floor(Math.random() * 20),
userInput: ''
});
}
}
handleChange(event) {
this.setState({
userInput: event.target.value
});
}
render() {
const possibleAnswers = [
'It is certain',
'It is decidedly so',
'Without a doubt',
'Yes, definitely',
'You may rely on it',
'As I see it, yes',
'Outlook good',
'Yes',
'Signs point to yes',
'Reply hazy try again',
'Ask again later',
'Better not tell you now',
'Cannot predict now',
'Concentrate and ask again',
"Don't count on it",
'My reply is no',
'My sources say no',
'Most likely',
'Outlook not so good',
'Very doubtful'
];
const answer = 'change me!'; // Change this line
return (
<div>
<input
type='text'
value={this.state.userInput}
onChange={this.handleChange}
style={inputStyle}
/>
<br />
<button onClick={this.ask}>Ask the Magic Eight Ball!</button>
<br />
<h3>Answer:</h3>
<p>
{/* Change code below this line */}
{/* Change code above this line */}
</p>
</div>
);
}
}
```
# --solutions--
```jsx
const inputStyle = {
width: 235,
margin: 5
};
class MagicEightBall extends React.Component {
constructor(props) {
super(props);
this.state = {
userInput: '',
randomIndex: ''
};
this.ask = this.ask.bind(this);
this.handleChange = this.handleChange.bind(this);
}
ask() {
if (this.state.userInput) {
this.setState({
randomIndex: Math.floor(Math.random() * 20),
userInput: ''
});
}
}
handleChange(event) {
this.setState({
userInput: event.target.value
});
}
render() {
const possibleAnswers = [
'It is certain',
'It is decidedly so',
'Without a doubt',
'Yes, definitely',
'You may rely on it',
'As I see it, yes',
'Outlook good',
'Yes',
'Signs point to yes',
'Reply hazy try again',
'Ask again later',
'Better not tell you now',
'Cannot predict now',
'Concentrate and ask again',
"Don't count on it",
'My reply is no',
'My sources say no',
'Outlook not so good',
'Very doubtful',
'Most likely'
];
const answer = possibleAnswers[this.state.randomIndex];
return (
<div>
<input
type='text'
value={this.state.userInput}
onChange={this.handleChange}
style={inputStyle}
/>
<br />
<button onClick={this.ask}>Ask the Magic Eight Ball!</button>
<br />
<h3>Answer:</h3>
<p>{answer}</p>
</div>
);
}
}
```

View File

@@ -0,0 +1,236 @@
---
id: 5a24c314108439a4d403618c
title: Використання Array.filter() для динамічної фільтрації масиву
challengeType: 6
forumTopicId: 301416
dashedName: use-array-filter-to-dynamically-filter-an-array
---
# --description--
Метод масиву `map` - це потужний інструмент, який ви будете часто використовувати під час роботи з React. Інший метод, пов'язаний з `map`, - це `filter`, який фільтрує зміст масиву на основі умови, а потім повертає новий масив. Наприклад, якщо у вас є масив користувачів і всі вони мають властивість `online`, яку можна встановити як `true` або `false`, то ви можете відфільтрувати тільки тих користувачів, які знаходяться в мережі, за допомогою запису:
```js
let onlineUsers = users.filter(user => user.online);
```
# --instructions--
В редакторі коду, `state` у `MyComponent` ініціалізується масивом користувачів. Деякі з користувачів знаходяться в мережі, а деякі ні. Відфільтруйте масив, щоб побачити лише користувачів, які знаходяться в мережі. Для цього, спочатку скористайтеся `filter`, щоб повернути новий масив, що містить лише тих користувачів, у котрих властивість `online` встановлена `true`. Потім у змінній `renderOnline` застосуйте відфільтрований масив та поверніть елемент `li` для кожного користувача, що містить текст їх `username`. Обов'язково вкажіть унікальний `key`, як і в останніх завданнях.
# --hints--
`MyComponent` має існувати та відображатись.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(MyComponent)).find('MyComponent').length,
1
);
```
стан `MyComponent` має бути ініціалізовано для масиву з шести користувачів.
```js
assert(
Array.isArray(
Enzyme.mount(React.createElement(MyComponent)).state('users')
) === true &&
Enzyme.mount(React.createElement(MyComponent)).state('users').length === 6
);
```
`MyComponent` має повернути `div`, `h1`, а потім невпорядкований список, що містить елементи `li` для кожного користувача, чий онлайн-статус встановлено `true`.
```js
(() => {
const comp = Enzyme.mount(React.createElement(MyComponent));
const users = (bool) => ({
users: [
{ username: 'Jeff', online: bool },
{ username: 'Alan', online: bool },
{ username: 'Mary', online: bool },
{ username: 'Jim', online: bool },
{ username: 'Laura', online: bool }
]
});
const result = () => comp.find('li').length;
const _1 = result();
const _2 = () => {
comp.setState(users(true));
return result();
};
const _3 = () => {
comp.setState(users(false));
return result();
};
const _4 = () => {
comp.setState({ users: [] });
return result();
};
const _2_val = _2();
const _3_val = _3();
const _4_val = _4();
assert(
comp.find('div').length === 1 &&
comp.find('h1').length === 1 &&
comp.find('ul').length === 1 &&
_1 === 4 &&
_2_val === 5 &&
_3_val === 0 &&
_4_val === 0
);
})();
```
`MyComponent` має виводити елемент `li`, що містить `username` кожного користувача, який знаходиться в мережі.
```js
(() => {
const comp = Enzyme.mount(React.createElement(MyComponent));
const users = (bool) => ({
users: [
{ username: 'Jeff', online: bool },
{ username: 'Alan', online: bool },
{ username: 'Mary', online: bool },
{ username: 'Jim', online: bool },
{ username: 'Laura', online: bool }
]
});
const ul = () => {
comp.setState(users(true));
return comp.find('ul').html();
};
const html = ul();
assert(
html ===
'<ul><li>Jeff</li><li>Alan</li><li>Mary</li><li>Jim</li><li>Laura</li></ul>'
);
})();
```
Кожний елемент списку повинен мати унікальний атрибут `key`.
```js
assert(
(() => {
const ul = Enzyme.mount(React.createElement(MyComponent)).find('ul');
console.log(ul.debug());
const keys = new Set([
ul.childAt(0).key(),
ul.childAt(1).key(),
ul.childAt(2).key(),
ul.childAt(3).key()
]);
return keys.size === 4;
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [
{
username: 'Jeff',
online: true
},
{
username: 'Alan',
online: false
},
{
username: 'Mary',
online: true
},
{
username: 'Jim',
online: false
},
{
username: 'Sara',
online: true
},
{
username: 'Laura',
online: true
}
]
};
}
render() {
const usersOnline = null; // Change this line
const renderOnline = null; // Change this line
return (
<div>
<h1>Current Online Users:</h1>
<ul>{renderOnline}</ul>
</div>
);
}
}
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [
{
username: 'Jeff',
online: true
},
{
username: 'Alan',
online: false
},
{
username: 'Mary',
online: true
},
{
username: 'Jim',
online: false
},
{
username: 'Sara',
online: true
},
{
username: 'Laura',
online: true
}
]
};
}
render() {
const usersOnline = this.state.users.filter(user => {
return user.online;
});
const renderOnline = usersOnline.map(user => {
return <li key={user.username}>{user.username}</li>;
});
return (
<div>
<h1>Current Online Users:</h1>
<ul>{renderOnline}</ul>
</div>
);
}
}
```

View File

@@ -0,0 +1,265 @@
---
id: 5a24c314108439a4d403618a
title: Використовуйте Array.map() для динамічної візуалізації елементів
challengeType: 6
forumTopicId: 301417
dashedName: use-array-map-to-dynamically-render-elements
---
# --description--
Умовне відображення зручне, але компоненти мають відображати невідому кількість елементів. Часто під час реактивного програмування у програміста не має можливості дізнатися стан додатку до запуску, бо дуже багато залежить від взаємодії користувача з програмою. Програміст має написати код, щоб правильно обробити цей невідомий стан до запуску. Використання `Array.map()` в React ілюструє це поняття.
Наприклад, ви створюєте простий додаток "To Do List". Як програміст, ви не можете знати, скільки пунктів має бути у переліку користувача. Треба вам потрібно налаштувати компонент на динамічний показ вірної кількості елементів переліку задовго до того, як користувач вирішить присвятити день пранню.
# --instructions--
У редакторі вже встановлено більшість компонентів `MyToDoList`. Дещо з цього коду може виглядати знайомо, якщо ви виконали завдання з контрольованої форми. Можна помітити `textarea` і `button`, так само як і кільки методів, що відстежують стан, але на сторінці ще нічого немає.
У `constructor` створіть об'єкт `this.state` і виділіть 2 стани: `userInput` має ініціалізуватися як порожній рядок, а `toDoList`- як порожній масив. Далі видаліть коментар з методу `render()` поруч зі змінною `items`. Натомість нанесіть на карту масив `toDoList`, збережений у внутрішньому стані компонента і динамічно виведіть `li` для кожного об'єкта. Спробуйте відобразити рядок `eat, code, sleep, repeat` у `textarea`, а потім натисніть на кнопку і подивіться, що трапиться.
**Note:** Ви, можливо, знаєте, що всі дочірні елементи, створені за допомогою такої операції, мають бути оснащені унікальним атрибутом `key`. Не хвилюйтесь, це тема наступного завдання.
# --hints--
Компонент MyToDoList має існувати і відображатися на сторінці.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
return mockedComponent.find('MyToDoList').length === 1;
})()
);
```
Перший дочірній елемент `MyToDoList` має бути елементом `textarea`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
return (
mockedComponent.find('MyToDoList').children().childAt(0).type() ===
'textarea'
);
})()
);
```
Другим дочірнім елементом `MyToDoList` має бути елемент `br`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
return (
mockedComponent.find('MyToDoList').children().childAt(1).type() === 'br'
);
})()
);
```
Третім дочірнім елементом `MyToDoList` має стати елемент `button`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
return (
mockedComponent.find('MyToDoList').children().childAt(2).type() ===
'button'
);
})()
);
```
Стан `MyToDoList` має бути ініціалізовано з `toDoList` як порожній масив.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
const initialState = mockedComponent.state();
return (
Array.isArray(initialState.toDoList) === true &&
initialState.toDoList.length === 0
);
})()
);
```
Стан `MyToDoList` має бути ініціалізовано з `userInput` як порожній масив.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
const initialState = mockedComponent.state();
return (
typeof initialState.userInput === 'string' &&
initialState.userInput.length === 0
);
})()
);
```
Коли натиснуто кнопку `Create List`, компонент `MyToDoList` має динамічно видати невпорядкований перелік, що містить елемент частини списку для кожної відокремленої комою частини списку, що вводиться в елемент `textarea`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
const simulateChange = (el, value) =>
el.simulate('change', { target: { value } });
const state_1 = () => {
return mockedComponent.find('ul').find('li');
};
const setInput = () => {
return simulateChange(
mockedComponent.find('textarea'),
'testA, testB, testC'
);
};
const click = () => {
return mockedComponent.find('button').simulate('click');
};
const state_2 = () => {
const nodes = mockedComponent.find('ul').find('li');
return { nodes, text: nodes.reduce((t, n) => t + n.text().trim(), '') };
};
const setInput_2 = () => {
return simulateChange(
mockedComponent.find('textarea'),
't1, t2, t3, t4, t5, t6'
);
};
const click_1 = () => {
return mockedComponent.find('button').simulate('click');
};
const state_3 = () => {
const nodes = mockedComponent.find('ul').find('li');
return { nodes, text: nodes.reduce((t, n) => t + n.text().trim(), '') };
};
const awaited_state_1 = state_1();
const awaited_setInput = setInput();
const awaited_click = click();
const awaited_state_2 = state_2();
const awaited_setInput_2 = setInput_2();
const awaited_click_1 = click_1();
const awaited_state_3 = state_3();
assert(
awaited_state_1.length === 0 &&
awaited_state_2.nodes.length === 3 &&
awaited_state_3.nodes.length === 6 &&
awaited_state_2.text === 'testAtestBtestC' &&
awaited_state_3.text === 't1t2t3t4t5t6'
);
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyToDoList />, document.getElementById('root'));
```
## --seed-contents--
```jsx
const textAreaStyles = {
width: 235,
margin: 5
};
class MyToDoList extends React.Component {
constructor(props) {
super(props);
// Change code below this line
// Change code above this line
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleSubmit() {
const itemsArray = this.state.userInput.split(',');
this.setState({
toDoList: itemsArray
});
}
handleChange(e) {
this.setState({
userInput: e.target.value
});
}
render() {
const items = null; // Change this line
return (
<div>
<textarea
onChange={this.handleChange}
value={this.state.userInput}
style={textAreaStyles}
placeholder='Separate Items With Commas'
/>
<br />
<button onClick={this.handleSubmit}>Create List</button>
<h1>My "To Do" List:</h1>
<ul>{items}</ul>
</div>
);
}
}
```
# --solutions--
```jsx
const textAreaStyles = {
width: 235,
margin: 5
};
class MyToDoList extends React.Component {
constructor(props) {
super(props);
this.state = {
toDoList: [],
userInput: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleSubmit() {
const itemsArray = this.state.userInput.split(',');
this.setState({
toDoList: itemsArray
});
}
handleChange(e) {
this.setState({
userInput: e.target.value
});
}
render() {
const items = this.state.toDoList.map((item, i) => {
return <li key={i}>{item}</li>;
});
return (
<div>
<textarea
onChange={this.handleChange}
value={this.state.userInput}
style={textAreaStyles}
placeholder='Separate Items With Commas'
/>
<br />
<button onClick={this.handleSubmit}>Create List</button>
<h1>My "To Do" List:</h1>
<ul>{items}</ul>
</div>
);
}
}
```

View File

@@ -0,0 +1,78 @@
---
id: 5a24c314108439a4d403616b
title: Використання реквізитів (props) за замовчуванням
challengeType: 6
forumTopicId: 301418
dashedName: use-default-props
---
# --description--
React також має опцію, щоб встановити реквізити за замовчуванням. Ви можете присвоїти реквізити за замовчуванням компоненту, як властивості самого компоненту, і React присвоює реквізит за замовчуванням, якщо це необхідно. Це дозволить вам визначити, яке значення реквізитів має бути, якщо ті, що без значення, є чітко визначеними. На приклад, якщо ви задаєте `MyComponent.defaultProps = { location: 'San Francisco' }`, то ви маєте визначене місцерозташування , яке встановлене у рядку `San Francisco`, якщо тільки ви не вкажете по-іншому. React призначає реквізити за замовчуванням, якщо об'єкти є невизначені, але якщо ви використаєте `null` як значення для реквізиту, то `null` залишається незмінним.
# --instructions--
Редактор коду нам показує компонент `ShoppingCart`. Визначте реквізити за замовчуванням для цього компонента, які визначають реквізит `items` разом із значенням `0`.
# --hints--
Компонент `ShoppingCart` має візуалізуватися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return mockedComponent.find('ShoppingCart').length === 1;
})()
);
```
Компонент `ShoppingCart` повинен мати реквізит за замовчуванням `{ items: 0 }`.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
mockedComponent.setProps({ items: undefined });
return mockedComponent.find('ShoppingCart').props().items === 0;
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<ShoppingCart />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const ShoppingCart = (props) => {
return (
<div>
<h1>Shopping Cart Component</h1>
</div>
)
};
// Change code below this line
```
# --solutions--
```jsx
const ShoppingCart = (props) => {
return (
<div>
<h1>Shopping Cart Component</h1>
</div>
)
};
// Change code below this line
ShoppingCart.defaultProps = {
items: 0
}
```

View File

@@ -0,0 +1,132 @@
---
id: 5a24c314108439a4d403616d
title: Використання PropTypes для визначення очікуваних параметрів
challengeType: 6
forumTopicId: 301419
dashedName: use-proptypes-to-define-the-props-you-expect
---
# --description--
React має корисні функції перевірки типу параметра, що отримує компонент. Наприклад, програма робить АРІ виклик, щоб отримати дані з масиву, які потім передаються до компонента як параметр. Можна дати команду `propTypes`, щоб зібрати дані типу `array`. Так ви отримаєте корисне повідомлення, якщо дані будуть іншого типу.
Краще за все встановлювати `propTypes`, коли знаєте заздалегідь, яким буде тип параметрів. Можна встановити властивість `propTypes` для компонента, так само як і `defaultProps`. Таким чином відбудеться перевірка, чи має параметр даного ключа конкретний тип. Ось приклад запиту типу `function` для параметра `handleClick`:
```js
MyComponent.propTypes = { handleClick: PropTypes.func.isRequired }
```
У даному прикладі, частина `PropTypes.func` перевіряє, чи є `handleClick` функцією. Додавання `isRequired` говорить React, що `handleClick` - це відповідне значення для даного компонента. З'явиться повідомлення, якщо цього параметру не буде. Також зверніть увагу, що `func` означає `function`. Серед семи простих типів JavaScript, лише `function` й `boolean` (написаний як `bool`) пишуться незвично. На додаток до цих простих типів, є й інші. Наприклад, можете перевірити, чи є параметр елементом React. Будь ласка, зверніться до [документації](https://reactjs.org/docs/typechecking-with-proptypes.html#proptypes), щоб дізнатися про інші варіанти.
**Note:** у React v15.5.0 `PropTypes` імпортується незалежно від React, ось так: `import PropTypes from 'prop-types';`
# --instructions--
Визначте `Items` для компонента `propTypes`, що потребує параметра `quantity` та підтвердіть, що його тип є `number`.
# --hints--
Компонент `ShoppingCart` має відображатися.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return mockedComponent.find('ShoppingCart').length === 1;
})()
);
```
Компонент `Items` має відображатись.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
return mockedComponent.find('Items').length === 1;
})()
);
```
Компонент `Items` має перевіряти `propTypes` значення `quantity` та гарантувати, що це значення є числом.
```js
(getUserInput) =>
assert(
(function () {
const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index'));
return (
noWhiteSpace.includes('quantity:PropTypes.number.isRequired') &&
noWhiteSpace.includes('Items.propTypes=')
);
})()
);
```
# --seed--
## --before-user-code--
```jsx
var PropTypes = {
number: { isRequired: true }
};
```
## --after-user-code--
```jsx
ReactDOM.render(<ShoppingCart />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const Items = (props) => {
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
};
// Change code below this line
// Change code above this line
Items.defaultProps = {
quantity: 0
};
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
return <Items />
}
};
```
# --solutions--
```jsx
const Items = (props) => {
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
};
// Change code below this line
Items.propTypes = {
quantity: PropTypes.number.isRequired
};
// Change code above this line
Items.defaultProps = {
quantity: 0
};
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
return <Items />
}
};
```

View File

@@ -0,0 +1,150 @@
---
id: 5a24c314108439a4d4036165
title: Використання React для рендерингу вкладених компонентів
challengeType: 6
forumTopicId: 301420
dashedName: use-react-to-render-nested-components
---
# --description--
В останньому завданні представлено простий спосіб складання компонентів, та за допомогою React їх можна складати по-іншому.
Композиція компонентів - одна з потужних функцій React. У роботі з React важливо сприймати інтерфейс користувача у вигляді її компонентів, таких як App з останнього завдання. Розбийте ІК на основні структурні блоки, ці частини і будуть компонентами. Це допоможе відокремити код, що відповідає за ІК, від коду, що відповідає за обчислення логіки програми. Так можна значно спростити розробку і обслуговування складних проєктів.
# --instructions--
У редакторі коду є два функціональні компоненти - `TypesOfFruit` та `Fruits`. Візьміть компонент `TypesOfFruit` та вкладіть або *nest* його у компонент `Fruits`. Потім візьміть компонент `Fruits` та вкладіть його у компонент `TypesOfFood`. У результаті має вийти дочірній компонент, вкладений у батьківський компонент, що вкладений у власний батьківській компонент!
# --hints--
Компонент `TypesOfFood` повинен відображати одинарний елемент `div`.
```js
assert(Enzyme.shallow(React.createElement(TypesOfFood)).type() === 'div');
```
Компонент `TypesOfFood` повинен відображати компонент `Fruits`.
```js
assert(
Enzyme.shallow(React.createElement(TypesOfFood)).props().children[1].type
.name === 'Fruits'
);
```
Компонент `Fruits` повинен відображати компонент `TypesOfFruit`.
```js
assert(
Enzyme.mount(React.createElement(TypesOfFood)).find('h2').html() ===
'<h2>Fruits:</h2>'
);
```
Компонент `TypesOfFruit` повинен відображати елементи `h2` та `ul`.
```js
assert(
Enzyme.mount(React.createElement(TypesOfFood)).find('ul').text() ===
'ApplesBlueberriesStrawberriesBananas'
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<TypesOfFood />, document.getElementById('root'))
```
## --seed-contents--
```jsx
const TypesOfFruit = () => {
return (
<div>
<h2>Fruits:</h2>
<ul>
<li>Apples</li>
<li>Blueberries</li>
<li>Strawberries</li>
<li>Bananas</li>
</ul>
</div>
);
};
const Fruits = () => {
return (
<div>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
};
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{ /* Change code below this line */ }
{ /* Change code above this line */ }
</div>
);
}
};
```
# --solutions--
```jsx
const TypesOfFruit = () => {
return (
<div>
<h2>Fruits:</h2>
<ul>
<li>Apples</li>
<li>Blueberries</li>
<li>Strawberries</li>
<li>Bananas</li>
</ul>
</div>
);
};
const Fruits = () => {
return (
<div>
{ /* Change code below this line */ }
<TypesOfFruit />
{ /* Change code above this line */ }
</div>
);
};
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{ /* Change code below this line */ }
<Fruits />
{ /* Change code above this line */ }
</div>
);
}
};
```

View File

@@ -0,0 +1,189 @@
---
id: 5a24c314108439a4d4036176
title: Використання стану для перемикання елемента
challengeType: 6
forumTopicId: 301421
dashedName: use-state-to-toggle-an-element
---
# --description--
Іноді, під час оновлення стану, вам необхідно знати попередній стан. Однак, оновлення станів можуть бути асихронними. Це означає, що React може згрупувати кілька викликів `setState()` в єдине оновлення. Це означає, що не можна покладатися на попереднє значення `this.state` або `this.props` при розрахунку наступного. Тож, такий код використовувати не варто:
```jsx
this.setState({
counter: this.state.counter + this.props.increment
});
```
Натомість, передайте `setState` функцію, що дає можливість отримати доступ до стану й пропсів. Використання функції за допомогою `setState` гарантує, що ви працюєте з найбільш актуальними значеннями стану й пропсами. Це означає, що вищенаведений код варто переписати так:
```jsx
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
```
Також, можна використати форму без `props`, якщо вам потрібен лише `state`:
```jsx
this.setState(state => ({
counter: state.counter + 1
}));
```
Зверніть увагу, що ви маєте буквально огорнути об'єкт дужками, бо інакше JavaScript вважатиме його блоком коду.
# --instructions--
`MyComponent` має властивість `visibility`, яка ініціалізується до `false`. Метод візуалізації має одне зображення, якщо значення `visibility` - правильне, та інше, якщо воно хибне.
Наразі немає способу оновлення властивостей `visibility` у `state`. Значення слід перемикати назад і вперед між правильним та хибним. Під час натискання на кнопку з'являється обробник натискань, який викликає класовий метод під назвою `toggleVisibility()`. Передайте функцію до `setState`, щоб встановити цей метод, і таким чином, коли метод застосується, `state` `visibility` перемикнеться на протилежне значення. Якщо `visibility` це `false`, цей метод змінить їх на `true` і навпаки.
Нарешті, натисніть на кнопку, щоб побачити умовне зображення компоненту, що базується на його `state`.
**Hint:** Не забудьте прив'язати `this` ключове слово до методу в `constructor`!
# --hints--
`MyComponent` має повернути `div` елемент, що містить `button`.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(MyComponent)).find('div').find('button')
.length,
1
);
```
Стан `MyComponent` має ініціалізуватись з властивістю `visibility` встановленою на `false`.
```js
assert.strictEqual(
Enzyme.mount(React.createElement(MyComponent)).state('visibility'),
false
);
```
При натисканні на цю кнопку елемент має встановити властивість `visibility` в стан між `true` і `false`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ visibility: false });
return mockedComponent.state('visibility');
};
const second = () => {
mockedComponent.find('button').simulate('click');
return mockedComponent.state('visibility');
};
const third = () => {
mockedComponent.find('button').simulate('click');
return mockedComponent.state('visibility');
};
const firstValue = first();
const secondValue = second();
const thirdValue = third();
assert(!firstValue && secondValue && !thirdValue);
})();
```
Анонімна функція має бути передана до `setState`.
```js
const paramRegex = '[a-zA-Z$_]\\w*(,[a-zA-Z$_]\\w*)?';
assert(
new RegExp(
'this\\.setState\\((function\\(' +
paramRegex +
'\\){|([a-zA-Z$_]\\w*|\\(' +
paramRegex +
'\\))=>)'
).test(__helpers.removeWhiteSpace(code))
);
```
`this` не можна використовувати всередині `setState`
```js
assert(!/this\.setState\([^}]*this/.test(code));
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
visibility: false
};
// Change code below this line
// Change code above this line
}
// Change code below this line
// Change code above this line
render() {
if (this.state.visibility) {
return (
<div>
<button onClick={this.toggleVisibility}>Click Me</button>
<h1>Now you see me!</h1>
</div>
);
} else {
return (
<div>
<button onClick={this.toggleVisibility}>Click Me</button>
</div>
);
}
}
}
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
visibility: false
};
this.toggleVisibility = this.toggleVisibility.bind(this);
}
toggleVisibility() {
this.setState(state => ({
visibility: !state.visibility
}));
}
render() {
if (this.state.visibility) {
return (
<div>
<button onClick={this.toggleVisibility}>Click Me</button>
<h1>Now you see me!</h1>
</div>
);
} else {
return (
<div>
<button onClick={this.toggleVisibility}>Click Me</button>
</div>
);
}
}
}
```

View File

@@ -0,0 +1,127 @@
---
id: 5a24c314108439a4d403617d
title: Використання методу componentDidMount
challengeType: 6
forumTopicId: 301422
dashedName: use-the-lifecycle-method-componentdidmount
---
# --description--
Більшість веб-розробників в якийсь момент мають викликати кінцеву точку API, щоб отримати дані. Якщо ви працюєте у React, потрібно розуміти як виконати цю дію.
Найкращим рішенням буде розмістити виклик API, або будь-які інші виклики направлені до сервера у методі життєвого циклу `componentDidMount()`. Цей метод викликають після того, як компонент встановлено в DOM. Будь-які виклики `setState()` активують ререндер вашого компонента. Коли ви викликаєте API у цьому методі й встановлюєте стан відповідно до вихідних API даних, цей метод автоматично запустить оновлення.
# --instructions--
В `componentDidMount()` є імітація виклику API. Через 2.5 секунди він симулює виклик до сервера для отримання даних. У цьому прикладі йде запит на кількість поточних активних користувачів сайту. У рендер методі покажіть значення `activeUsers` у `h1` після тексту `Active Users:`. Подивіться, що відбувається при попередньому перегляді та спробуйте змінити таймаут, щоб побачити різні ефекти.
# --hints--
`MyComponent` має рендерити елемент `div`, що охоплює `h1` тег.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return (
mockedComponent.find('div').length === 1 &&
mockedComponent.find('h1').length === 1
);
})()
);
```
Стан компонентів має оновлюватися функцією таймаут у `componentDidMount`.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return new RegExp('setTimeout(.|\n)+setState(.|\n)+activeUsers').test(
String(mockedComponent.instance().componentDidMount)
);
})()
);
```
Тег `h1` має рендерити значення `activeUsers` зі стану `MyComponent`.
```js
(() => {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
const first = () => {
mockedComponent.setState({ activeUsers: 1237 });
return mockedComponent.find('h1').text();
};
const second = () => {
mockedComponent.setState({ activeUsers: 1000 });
return mockedComponent.find('h1').text();
};
assert(new RegExp('1237').test(first()) && new RegExp('1000').test(second()));
})();
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'));
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeUsers: null
};
}
componentDidMount() {
setTimeout(() => {
this.setState({
activeUsers: 1273
});
}, 2500);
}
render() {
return (
<div>
{/* Change code below this line */}
<h1>Active Users: </h1>
{/* Change code above this line */}
</div>
);
}
}
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeUsers: null
};
}
componentDidMount() {
setTimeout(() => {
this.setState({
activeUsers: 1273
});
}, 2500);
}
render() {
return (
<div>
<h1>Active Users: {this.state.activeUsers}</h1>
</div>
);
}
}
```

View File

@@ -0,0 +1,87 @@
---
id: 5a24c314108439a4d403617c
title: Використання методу життєвого циклу componentWillMount
challengeType: 6
forumTopicId: 301423
dashedName: use-the-lifecycle-method-componentwillmount
---
# --description--
Компоненти у React мають декілька спеціальних методів, що забезпечують можливість виконувати дії у визначений час у їхньому життєвому циклі. Вони називаються методами життєвого циклу, чи перехоплювачами життєвого циклу і дозволяють виловити компонент у визначений час. Це може бути до того, як вони будуть показані, перш ніж вони оновляться, перш ніж вони отримають реквізити, перш ніж від'єднати і так далі. Ось список декількох методів життєвого циклу: `componentWillMount()` `componentDidMount()` `shouldComponentUpdate()` `componentDidUpdate()` `componentWillUnmount()`. Протягом наступних уроків розглянемо деякі основні випадки використання даних методів.
**Примітка:** метод життєвого циклу `componentWillMount` буде виключено з майбутньої версії 16.X і видалено з версії 17. [(Джерело)](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html)
# --instructions--
Метод `componentWillMount()` викликається перед методом `render()`, коли компонент підключений до DOM. Запишіть щось в консоль в межах `componentWillMount()`; щоб побачити результат - відкрийте консоль вашого браузера.
# --hints--
`MyComponent` має відобразити `div` елемент.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('div').length === 1;
})()
);
```
`console.log` слід викликати у `componentWillMount`.
```js
assert(
(function () {
const lifecycle = React.createElement(MyComponent)
.type.prototype.componentWillMount.toString()
.replace(/ /g, '');
return lifecycle.includes('console.log(');
})()
);
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<MyComponent />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
// Change code below this line
// Change code above this line
}
render() {
return <div />
}
};
```
# --solutions--
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
// Change code below this line
console.log('Component is mounting...');
// Change code above this line
}
render() {
return <div />
}
};
```

View File

@@ -0,0 +1,84 @@
---
id: 5a24c314108439a4d4036168
title: Написання компонента React з нуля
challengeType: 6
forumTopicId: 301424
dashedName: write-a-react-component-from-scratch
---
# --description--
Тепер, коли ви вивчили основи компонентів JSX і React, прийшов час написати компонент самостійно. Компоненти React є основними складовими елементами додатків React, тому важливо добре ознайомитися з їх написанням. Пам'ятайте, типовий компонент React це ES6 `class`, який розширює `React.Component`. Він має метод візуалізації, що повертає HTML (від JSX) або `null`. Це основна форма компонента React. Як тільки ви це добре засвоїте, ви будете готові почати розробку більш складних проектів React.
# --instructions--
Визначте клас `MyComponent`, який розширює `React.Component`. Його метод візуалізації повинен повертатися як `div`, який містить теґ `h1` з текстом у ньому: `My First React Component!`. Використовуйте саме цей текст, регістр та пунктуація мають значення. Не забудьте також викликати конструктор для вашого компонента.
Надайте цей документ до DOM, використавши `ReactDOM.render()`. Можна скористатися `div` з `id='challenge-node'`.
# --hints--
Можна побачити компонент React під назвою `MyComponent`.
```js
(getUserInput) =>
assert(
__helpers
.removeWhiteSpace(getUserInput('index'))
.includes('classMyComponentextendsReact.Component{')
);
```
`MyComponent` має містити теґ `h1` з текстом `My First React Component!`. Регістр та пунктуація мають значення.
```js
assert(
(function () {
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
return mockedComponent.find('h1').text() === 'My First React Component!';
})()
);
```
`MyComponent` має бути переданим до DOM.
```js
assert(document.getElementById('challenge-node').childNodes.length === 1);
```
`MyComponent` має мати конструктор під назвою `super` з `props`.
```js
assert(
MyComponent.toString().includes('MyComponent(props)') &&
MyComponent.toString().includes('_super.call(this, props)')
);
```
# --seed--
## --seed-contents--
```jsx
// Change code below this line
```
# --solutions--
```jsx
// Change code below this line
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>My First React Component!</h1>
</div>
);
}
};
ReactDOM.render(<MyComponent />, document.getElementById('challenge-node'));
```

View File

@@ -0,0 +1,146 @@
---
id: 5a24c314108439a4d4036177
title: Введення простого лічильника
challengeType: 6
forumTopicId: 301425
dashedName: write-a-simple-counter
---
# --description--
Ви можете створити більш складний компонент, що зберігає стан, комбінуючи поняття, розглянуті раніше. Вони включають ініціалізацію `state`, методи запису, які встановлюють `state`, та призначення обробників кліків для запуску цих методів.
# --instructions--
Компонент `Counter` відстежує значення `count` в `state`. Існує дві кнопки для виклику методів `increment()` та `decrement()`. Напишіть ці методи так, щоб значення лічильника було збільшено або зменшено на 1 при натисканні відповідної кнопки. Крім того, створіть метод `reset()` так, щоб при натисканні кнопки рахунок дорівнював 0.
**Примітка:** Переконайтеся, що ви не змінили `className` кнопок. Також не забудьте додати необхідні прив'язки для новостворених методів в конструктор.
# --hints--
`Counter` повинен повертатися як елемент `div`, який містить три кнопки з текстовим вмістом у такому порядку: `Increment!`, `Decrement!`, `Reset`.
```js
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Counter));
return (
mockedComponent.find('.inc').text() === 'Increment!' &&
mockedComponent.find('.dec').text() === 'Decrement!' &&
mockedComponent.find('.reset').text() === 'Reset'
);
})()
);
```
Стан `Counter` має ініціалізуватись за допомогою властивості `count` з заданим значенням `0`.
```js
const mockedComponent = Enzyme.mount(React.createElement(Counter));
assert(mockedComponent.find('h1').text() === 'Current Count: 0');
```
Після натискання кнопки збільшення, рахунок має збільшитись на `1`.
```js
const mockedComponent = Enzyme.mount(React.createElement(Counter));
mockedComponent.find('.inc').simulate('click');
assert(mockedComponent.find('h1').text() === 'Current Count: 1');
```
Після натискання кнопки зменшення, рахунок має зменшитись на `1`.
```js
const mockedComponent = Enzyme.mount(React.createElement(Counter));
mockedComponent.find('.dec').simulate('click');
assert(mockedComponent.find('h1').text() === 'Current Count: -1');
```
Після натискання кнопки перезапуску рахунок дорівнюватиме `0`.
```js
const mockedComponent = Enzyme.mount(React.createElement(Counter));
mockedComponent.setState({ count: 5 });
const currentCountElement = mockedComponent.find('h1');
assert(currentCountElement.text() === 'Current Count: 5');
mockedComponent.find('.reset').simulate('click');
assert(currentCountElement.text() === 'Current Count: 0');
```
# --seed--
## --after-user-code--
```jsx
ReactDOM.render(<Counter />, document.getElementById('root'))
```
## --seed-contents--
```jsx
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
// Change code below this line
// Change code above this line
}
// Change code below this line
// Change code above this line
render() {
return (
<div>
<button className='inc' onClick={this.increment}>Increment!</button>
<button className='dec' onClick={this.decrement}>Decrement!</button>
<button className='reset' onClick={this.reset}>Reset</button>
<h1>Current Count: {this.state.count}</h1>
</div>
);
}
};
```
# --solutions--
```jsx
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.reset = this.reset.bind(this);
}
reset() {
this.setState({
count: 0
});
}
increment() {
this.setState(state => ({
count: state.count + 1
}));
}
decrement() {
this.setState(state => ({
count: state.count - 1
}));
}
render() {
return (
<div>
<button className='inc' onClick={this.increment}>Increment!</button>
<button className='dec' onClick={this.decrement}>Decrement!</button>
<button className='reset' onClick={this.reset}>Reset</button>
<h1>Current Count: {this.state.count}</h1>
</div>
);
}
};
```