Веб-приложение или сайт, который загружает только одну страницу и все последующие запросы обрабатываются без полной перезагрузки страницы
React — JavaScript-библиотека для создания пользовательских интерфейсов, которая позволяет довольно просто реализовывать и SPA в том числе
// Импортируем необходимые библиотеки
import React from 'react';
import ReactDOM from 'react-dom';
// Выбираем элемент, внутри которого мы хотим нарисовать форму
const root = document.getElementById('root');
// Создаем элемент формы
const form = React.createElement(
'div',
{ className: 'editor' },
// Элемент состоит из 3ех частей: имя тега, атрибуты, дочерние элементы
// HTML эквивалент: <input placeholder="Ключ заметки" />
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
// HTML эквивалент: <button>Сохранить</button>
React.createElement('button', null, 'Сохранить')
);
// Отображаем форму на экране
ReactDOM.render(form, root);
// Импортируем необходимые библиотеки
import React from 'react';
import ReactDOM from 'react-dom';
// Выбираем элемент, внутри которого мы хотим нарисовать форму
const root = document.getElementById('root');
// Создаем элемент формы
const form = React.createElement(
'div',
{ className: 'editor' },
// Элемент состоит из 3ех частей: имя тега, атрибуты, дочерние элементы
// HTML эквивалент: <input placeholder="Ключ заметки" />
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
// HTML эквивалент: <button>Сохранить</button>
React.createElement('button', null, 'Сохранить')
);
// Отображаем форму на экране
ReactDOM.render(form, root);
// Импортируем необходимые библиотеки
import React from 'react';
import ReactDOM from 'react-dom';
// Выбираем элемент, внутри которого мы хотим нарисовать форму
const root = document.getElementById('root');
// Создаем элемент формы
const form = React.createElement(
'div',
{ className: 'editor' },
// Элемент состоит из 3ех частей: имя тега, атрибуты, дочерние элементы
// HTML эквивалент: <input placeholder="Ключ заметки" />
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
// HTML эквивалент: <button>Сохранить</button>
React.createElement('button', null, 'Сохранить')
);
// Отображаем форму на экране
ReactDOM.render(form, root);
// Импортируем необходимые библиотеки
import React from 'react';
import ReactDOM from 'react-dom';
// Выбираем элемент, внутри которого мы хотим нарисовать форму
const root = document.getElementById('root');
// Создаем элемент формы целиком
const form = React.createElement(
'div',
{ className: 'editor' },
// Элемент состоит из 3ех частей: имя тега, атрибуты, дочерние элементы
// HTML эквивалент: <input placeholder="Ключ заметки" />
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
// HTML эквивалент: <button>Сохранить</button>
React.createElement('button', null, 'Сохранить')
);
// Отображаем форму на экране
ReactDOM.render(form, root);
// Импортируем необходимые библиотеки
import React from 'react';
import ReactDOM from 'react-dom';
// Выбираем элемент, внутри которого мы хотим нарисовать форму
const root = document.getElementById('root');
// Создаем элемент формы
const form = React.createElement(
'div',
{ className: 'editor' },
// Элемент состоит из 3ех частей: имя тега, атрибуты, дочерние элементы
// HTML эквивалент: <input placeholder="Ключ заметки" />
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
// HTML эквивалент: <button>Сохранить</button>
React.createElement('button', null, 'Сохранить')
);
// Отображаем форму на экране
ReactDOM.render(form, root);
// Импортируем необходимые библиотеки
import React from 'react';
import ReactDOM from 'react-dom';
// Выбираем элемент, внутри которого мы хотим нарисовать форму
const root = document.getElementById('root');
// Создаем элемент формы
const form = React.createElement(
'div',
{ className: 'editor' },
// Элемент состоит из 3ех частей: имя тега, атрибуты, дочерние элементы
// HTML эквивалент: <input placeholder="Ключ заметки" />
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
// HTML эквивалент: <button>Сохранить</button>
React.createElement('button', null, 'Сохранить')
);
// Отображаем форму на экране
ReactDOM.render(form, root);
const element = Что такое JSX?
;
JSX — это расширение языка JavaScript.
Позволяет нагляднее создавать элементы.
<select multiple>
<option value="Пункт 1">Пункт 1</option>
<option selected value="Пункт 2">Пункт 2</option>
</select>
VS
React.createElement(
'select',
{ multiple: true },
React.createElement(
'option',
{ value: 'Пункт 1' },
'Пункт 1'
),
React.createElement(
'option',
{ selected: true, value: 'Пункт 2' },
'Пункт 2'
)
);
Так как JSX превратится в валидный JavaScript, то в нем можно использовать любые JavaScript выражения
const user = { name: 'Студент' };
const element = <div>Привет, {user.name}</div>;
const html = '<strong>Мир</strong>';
const element = <div>Привет, {html}</div>;
Привет, <strong>Мир</strong>
const html = 'Привет, <strong>Мир</strong>!';
const element = (
<div dangerouslySetInnerHTML={{ __html: html }} />
);
Привет, Мир!
Компоненты предоставляют механизм разбиения интерфейса на небольшие независимые части, которые реализуются по отдельности
Пример
const root = document.getElementById('root');
ReactDOM.render(
// Весь код, отвечающий за форму, можно вынести в компонент
<div className="editor">
<input placeholder="Ключ заметки" />
<textarea placeholder="Текст заметки" />
<button>Отменить</button>
<button>Сохранить</button>
</div>,
root
);
Пример
const root = document.getElementById('root');
ReactDOM.render(
// Весь код, отвечающий за форму, можно вынести в компонент
<div className="editor">
<input placeholder="Ключ заметки" />
<textarea placeholder="Текст заметки" />
<button>Отменить</button>
<button>Сохранить</button>
</div>,
root
);
Почти всё в приложении, написанном на React, будет компонентом. Неважно что это — форма, компонент заметки или целая страница
Это становится возможным благодаря механизму композиции или объединения компонентов. Любой компонент в своей реализации может использовать любой другой компонент
import Editor from './Editor';
import Notes from './Notes';
function NotesApp() {
return (
<div className="notes-app">
<Editor />
<Notes />
</div>
);
}
Любой компонент может получать на вход пропсы, подобно тому как функции принимают аргументы, а html-элементы атрибуты
Именно просы позволяют делать компоненты универсальными
function Notes() {
return (
<div className="notes">
<Note name="Books" text="Books to read" />
<Note name="Music" text="Music to listen" />
<Note name="Films" text="Films to watch" />
</div>
);
}
В компонентах пропсы доступны в объекте, который будет передан в первый аргумент функции-компонента
function Note(props) {
return (
<div className="note">
<h1>{props.name}</h1>
<p>{props.text}</p>
</div>
);
}
children - это зарезервированное название одного из пропсов
Этот механизм позволяет передавать дочерние элементы более наглядно и явно, подобно тому как это делается в HTML
function Notes() {
return (
<div className="notes">
<Note name="Books">
Books to read
</Note>
<Note name="Films">
<p>Films to read</p>
<button>Like</button>
</Note>
...
</div>
);
}
import React from 'react';
function Note(props) {
return (
<div className="note">
<h1 className="note__title">
{props.name}
</h1>
<div className="note__content">
{props.children}
</div>
</div>
);
}
import React from 'react';
function Note(props) {
return (
<div className="note">
<h1 className="note__title">
{props.name}
</h1>
<div className="note__content">
{props.children}
</div>
</div>
);
}
Так как JSX позволяет использовать произвольные JavaScript выражения, то проблемы необходимости рендеринга в зависимости от тех или иных условий решаются стандартными средствами языка
function Note(props) {
return (
<div className="note">
<h2 className="note__title">
{props.type === 'warning'
? 'Warning'
: props.name
}
</h2>
...
</div>
);
}
function Note(props) {
return (
<div className="note">
<h2 className="note__title">
{props.type === 'warning'
? 'Warning'
: props.name
}
</h2>
...
</div>
);
}
Проблемы со списками однотипных элементов решаются аналогично
function NotesList() {
return (
<div className="notes-list">
<Note name="Books" text="Books to read" />
<Note name="Films" text="Films to watch" />
<Note name="Music" text="Music to listen" />
</div>
);
}
function NotesList({ notes }) {
return (
<div className="notes-list">
{notes.map(note => (
<Note name={note.name} text={note.text} />
))}
</div>
);
}
Однако, при рендеринге, нас ожидает ошибка в консоли браузера
Each child in an array or iterator should have a unique "key" prop.
function NotesList({ notes }) {
return (
<div className="notes-list">
{notes.map(note => (
<Note
name={note.name}
text={note.text}
key={note.id} // Избавляемся от ошибки
/>
))}
</div>
);
}
Нужно добавить уникальный пропс key к каждому из элементов списка
Ключи (keys) помогают React определять, какие элементы были изменены, добавлены или удалены. Их необходимо указывать, чтобы React мог сопоставлять элементы массива с течением времени
Состояние компонента — это механизм, который позволяет сделать его «живым»
Основное отличие состояния от пропсов в том, что состояние доступно только самому компоненту
Любой компонент автоматически отреагирует на все изменения собственного состояния и будет перерисован
import { useState } from 'react';
function Note(props) {
const [isReadMoreClicked, setIsReadMoreClicked] = useState(false);
return (
<div className="note">
<div className="note__name">Books</div>
<div className="note__text">Books to read</div>
{isReadMoreClicked
? <div className="note__additional-text">Additional text</div>
: <button onClick={() => setIsReadMoreClicked(true)}>Read more</button>
}
</div>
);
}
import { useState } from 'react';
function Note(props) {
const [isReadMoreClicked, setIsReadMoreClicked] = useState(false);
return (
<div className="note">
<div className="note__name">Books</div>
<div className="note__text">Books to read</div>
{isReadMoreClicked
? <div className="note__additional-text">Additional text</div>
: <button onClick={() => setIsReadMoreClicked(true)}>Read more</button>
}
</div>
);
}
import { useState } from 'react';
function Note(props) {
const [isReadMoreClicked, setIsReadMoreClicked] = useState(false);
return (
<div className="note">
<div className="note__name">Books</div>
<div className="note__text">Books to read</div>
{isReadMoreClicked
? <div className="note__additional-text">Additional text</div>
: <button onClick={() => setIsReadMoreClicked(true)}>Read more</button>
}
</div>
);
}
import { useState } from 'react';
function Note(props) {
const [isReadMoreClicked, setIsReadMoreClicked] = useState(false);
return (
<div className="note">
<div className="note__name">Books</div>
<div className="note__text">Books to read</div>
{isReadMoreClicked
? <div className="note__additional-text">Additional text</div>
: <button onClick={() => setIsReadMoreClicked(true)}>Read more</button>
}
</div>
);
}
/**
* Неправильно, так как обновления будут сгруппированы и каждое
* из них обратится к старому значению counter.
* Как результат значение counter увеличится на 1, вместо 2
*/
setCounter(counter + 1);
setCounter(counter + 1);
/**
* Правильно. Передавая функцию в качестве аргумента, мы делаем
* изменения атомарными. Каждое из них получит актуальное значение состояния
*/
setCounter(counter => counter + 1);
setCounter(counter => counter + 1);
import React, { useRef } from 'react';
const AnyComponent = () => {
const ref = useRef(null);
const onSubmit = () => {
ref.current.submit();
};
return (
<form ref={ref}>...</form>
)
}
import React, { useMemo } from 'react';
const AnyComponent = (props) => {
const value = useMemo(
() => calculateAnyValue(props.a, state.b),
[props.a, state.b]
);
...
}
import React, { useMemo } from 'react';
const AnyComponent = (props) => {
const value = useMemo(
() => calculateAnyValue(props.a, state.b),
[props.a, state.b]
);
...
}
import React, { useMemo } from 'react';
const AnyComponent = (props) => {
const value = useMemo(
() => calculateAnyValue(props.a, state.b),
[props.a, state.b]
);
...
}
import React, { useMemo } from 'react';
const AnyComponent = (props) => {
const value = useMemo(
() => calculateAnyValue(props.a, state.b),
[props.a, state.b]
);
...
}
import React, { useCallback } from 'react';
const AnyComponent = ({ name }) => {
const value = useCallback(
e => sendParams(name, e.target.value),
[name]
);
...
}
const value = useCallback(
e => sendParams(name, e.target.value),
[name]
);
import React, { useEffect } from 'react';
const AnyComponent = () => {
useEffect(
() => {
...
},
[]
);
}
import React, { useEffect } from 'react';
const AnyComponent = ({ pageName }) => {
useEffect(
() => {
fetchAnyData(pageName);
}
);
}
import React, { useEffect } from 'react';
const AnyComponent = ({ pageName }) => {
useEffect(
() => {
fetchAnyData(pageName);
},
[]
);
}
const AnyComponent = () => {
useEffect(
() => {
const keyPressHandler = () => {
...
};
document.addEventListener('keypress', keyPressHandler);
return () => document.removeEventListener('keypress', keyPressHandler);
},
[]
);
}
const AnyComponent = (props) => {
useEffect(
() => {
const keyPressHandler = () => {
... // использует anyValue
};
document.addEventListener('keypress', keyPressHandler);
return () => document.removeEventListener('keypress', keyPressHandler);
},
[props.anyValue]
);
}
const useToggle = initialValue => {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(
() => setValue(value => !value),
[]
)
return [value, toggle];
}
const useToggle = initialValue => {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(
() => setValue(value => !value),
[]
)
return [value, toggle];
}
const useToggle = initialValue => {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(
() => setValue(value => !value),
[]
)
return [value, toggle];
}
const useSearch({ items, searchKey }) => {
const [searchValue, setSearchValue] = useState('');
const onChangeSearch = debounce(setSearchValue, 300);
const filteredItems = useMemo(
() => items.filter(
item => item[searchKey].includes(searchValue)
),
[items, searchValue, searchKey]
);
return { searchValue, onChangeSearch, filteredItems };
}
const useSearch({ items, searchKey }) => {
const [searchValue, setSearchValue] = useState('');
const onChangeSearch = debounce(setSearchValue, 300);
const filteredItems = useMemo(
() => items.filter(
item => item[searchKey].includes(searchValue)
),
[items, searchValue, searchKey]
);
return { searchValue, onChangeSearch, filteredItems };
}
const useSearch({ items, searchKey }) => {
const [searchValue, setSearchValue] = useState('');
const onChangeSearch = debounce(setSearchValue, 300);
const filteredItems = useMemo(
() => items.filter(
item => item[searchKey].includes(searchValue)
),
[items, searchValue, searchKey]
);
return { searchValue, onChangeSearch, filteredItems };
}
const useSearch({ items, searchKey }) => {
const [searchValue, setSearchValue] = useState('');
const onChangeSearch = debounce(setSearchValue, 300);
const filteredItems = useMemo(
() => items.filter(
item => item[searchKey].includes(searchValue)
),
[items, searchValue, searchKey]
);
return { searchValue, onChangeSearch, filteredItems };
}