Веб-приложение или сайт, который загружает только одну страницу и все последующие запросы обрабатываются без полной перезагрузки страницы
import React from 'react';
import ReactDOM from 'react-dom';
const root = document.getElementById('root');
ReactDOM.render(
React.createElement(
'div',
{ className: 'editor' },
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
React.createElement('button', null, 'Сохранить')
),
root
);
import React from 'react';
import ReactDOM from 'react-dom';
const root = document.getElementById('root');
ReactDOM.render(
React.createElement(
'div',
{ className: 'editor' },
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
React.createElement('button', null, 'Сохранить')
),
root
);
import React from 'react';
import ReactDOM from 'react-dom';
const root = document.getElementById('root');
ReactDOM.render(
React.createElement(
'div',
{ className: 'editor' },
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
React.createElement('button', null, 'Сохранить')
),
root
);
import React from 'react';
import ReactDOM from 'react-dom';
const root = document.getElementById('root');
ReactDOM.render(
React.createElement(
'div',
{ className: 'editor' },
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
React.createElement('button', null, 'Сохранить')
),
root
);
import React from 'react';
import ReactDOM from 'react-dom';
const root = document.getElementById('root');
ReactDOM.render(
React.createElement(
'div',
{ className: 'editor' },
React.createElement('input', { placeholder: 'Ключ заметки' }),
React.createElement('textarea', { placeholder: 'Текст заметки' }),
React.createElement('button', null, 'Отменить'),
React.createElement('button', null, 'Сохранить')
),
root
);
const element = Что такое JSX?
;
<select multiple>
<option value="Пункт 1">Пункт 1</option>
<option selected value="Пункт 2">Пункт 2</option>
</select>
React.createElement(
'select',
{ multiple: true },
React.createElement(
'option',
{ value: 'Пункт 1' },
'Пункт 1'
),
React.createElement(
'option',
{ selected: true, value: 'Пункт 2' },
'Пункт 2'
)
);
<input
class="name"
tabindex="2"
onchange="console.log('changed!');"
/>
<input
className="name"
tabIndex="2"
onChange={event => console.log('changed!')}
/>
<div>
<span>Введите ваше имя:</span><br>
<input type="text">
</div>
<div>
<span>Введите ваше имя:</span><br />
<input type="text" />
</div>
|
|
|
|
<p>Первый абзац</p>
<p>Второй абзац</p>
<div>
<p>Первый абзац</p>
<p>Второй абзац</p>
</div>
<React.Fragment>
<p>Первый абзац</p>
<p>Второй абзац</p>
</React.Fragment>
const user = { name: 'Максим' };
const element = <div>Привет, {user.name}</div>;
const html = '<strong>Мир</strong>';
const element = <div>Привет, {html}</div>;
const html = 'Привет, <strong>Мир</strong>!';
const element = (
<div dangerouslySetInnerHTML={{ __html: html }} />
);
import React from 'react';
import ReactDOM from 'react-dom';
const root = document.getElementById('root');
ReactDOM.render(
<div className="editor">
<input placeholder="Ключ заметки" />
<textarea placeholder="Текст заметки" />
<button>Отменить</button>
<button>Сохранить</button>
</div>,
root
);
import React from 'react';
function Editor() {
return (
<div className="editor">
<input placeholder="Ключ заметки" />
<textarea placeholder="Текст заметки" />
<button>Отменить</button>
<button>Сохранить</button>
</div>
);
}
import React, { Component } from 'react';
class Editor extends Component {
render() {
return (
<div className="editor">
<input placeholder="Ключ заметки" />
<textarea placeholder="Текст заметки" />
<button>Отменить</button>
<button>Сохранить</button>
</div>
);
}
}
import Editor from './Editor';
import Notes from './Notes';
function NotesApp() {
return (
<div className="notes-app">
<Editor />
<Notes />
</div>
);
}
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>
);
}
interface NoteProps {
name: string;
text: string;
}
function Note({ name, text }: NoteProps) {
return (
<div className="note">
<h1>{name}</h1>
<p>{text}</p>
</div>
);
}
import React, { Component } from 'react';
interface NoteProps {
name: string;
text: string;
}
class Note extends Component<NoteProps> {
render() {
return (
<div className="note">
<h1>{this.props.name}</h1>
<p>{this.props.text}</p>
</div>
);
}
}
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, { ReactNode } from 'react';
interface NoteProps {
children: ReactNode;
name: string;
}
function Note({ children, name }: NoteProps) {
return (
<div className="note">
<h1 className="note__title">
{name}
</h1>
<div className="note__content">
{children}
</div>
</div>
);
}
import React, { ReactNode } from 'react';
interface NoteProps {
children: ReactNode;
name: string;
}
function Note({ children, name }: NoteProps) {
return (
<div className="note">
<h1 className="note__title">
{name}
</h1>
<div className="note__content">
{children}
</div>
</div>
);
}
function Note({ children, name, type }: NoteProps) {
return (
<div className="note">
<h1 className="note__title">
{type === 'warning' ? 'Warning' : name}
</h1>
...
</div>
);
}
function Note({ children, name, type }: NoteProps) {
return (
<div className="note">
<h1 className="note__title">
{type === 'warning' ? 'Warning' : name}
</h1>
...
</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 }: NotesListProps) {
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.
Check the render method of NotesList. See https://fb.me/react-warning-keys for more information.
function NotesList({ notes }: NotesListProps) {
return (
<div className="notes-list">
{notes.map(note => (
<Note
name={note.name}
text={note.text}
key={note.id}
/>
))}
</div>
);
}
Each child in an array or iterator should have a unique "key" prop.
Check the render method of NotesList. See https://fb.me/react-warning-keys for more information.
interface NoteState {
isReadMoreClicked: boolean;
}
class Note extends Component<NoteProps, NoteState> {
state: NoteState = { isReadMoreClicked: false }
handleReadMoreClick = () => {
this.setState({ isReadMoreClicked: true });
}
...
}
interface NoteState {
isReadMoreClicked: boolean;
}
class Note extends Component<NoteProps, NoteState> {
state: NoteState = { isReadMoreClicked: false }
handleReadMoreClick = () => {
this.setState({ isReadMoreClicked: true });
}
...
}
interface NoteState {
isReadMoreClicked: boolean;
}
class Note extends Component<NoteProps, NoteState> {
state: NoteState = { isReadMoreClicked: false }
handleReadMoreClick = () => {
this.setState({ isReadMoreClicked: true });
}
...
}
interface NoteState {
isReadMoreClicked: boolean;
}
class Note extends Component<NoteProps, NoteState> {
state: NoteState = { isReadMoreClicked: false }
handleReadMoreClick = () => {
this.setState({ isReadMoreClicked: true });
}
...
}
class Note extends Component<NoteProps, NoteState> {
...
render() {
return (
<div className="note">
<div className="note__name">Books</div>
<div className="note__text">Books to read</div>
{this.state.isReadMoreClicked
? <button onClick={handleReadMoreClick}>Read more</button>
: <div className="note__additional-text">Additional text</div>
}
</div>
);
}
}
class Note extends Component<NoteProps, NoteState> {
...
render() {
return (
<div className="note">
<div className="note__name">Books</div>
<div className="note__text">Books to read</div>
{this.state.isReadMoreClicked
? <button onClick={handleReadMoreClick}>Read more</button>
: <div className="note__additional-text">Additional text</div>
}
</div>
);
}
}
// Неправильно
this.state.isReadMoreClicked = true;
// Правильно
this.setState({ isReadMoreClicked: true });
// Неправильно
this.setState({ counter: this.state.counter + 1 });
this.setState({ counter: this.state.counter + 1 });
// Правильно
this.setState(state => ({ counter: state.counter + 1 }));
this.setState(state => ({ counter: state.counter + 1 }));
interface State {
notes: Note[];
isReadMoreClicked: boolean;
}
this.setState({ isReadMoreClicked: true });
constructor
↓
(static) getDerivedStateFromProps
↓
render
↓
componentDidMount
(static) getDerivedStateFromProps
↓
shouldComponentUpdate
↓
render
↓
componentDidUpdate
shouldComponentUpdate
↓
render
↓
componentDidUpdate
componentWillUnmount
class Notes extends Component<NotesProps, NotesState> {
state: NotesState = { backToTop: false }
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
if (!this.state.backToTop && document.documentElement.scrollTop > 300) {
this.setState({ backToTop: true });
}
...
}
...
}
class Notes extends Component<NotesProps, NotesState> {
state: NotesState = { backToTop: false }
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
if (!this.state.backToTop && document.documentElement.scrollTop > 300) {
this.setState({ backToTop: true });
}
...
}
...
}
class Notes extends Component<NotesProps, NotesState> {
state: NotesState = { backToTop: false }
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
if (!this.state.backToTop && document.documentElement.scrollTop > 300) {
this.setState({ backToTop: true });
}
...
}
...
}
1. Неуправляемые | (Uncontrolled) |
2. Управляемые | (Controlled) |
DOM управляет текущим состоянием компонента
import React, { Component, createRef } from 'react';
class UncontrolledForm extends Component {
inputRef = createRef<HTMLInputElement>()
handleSubmit = () => {
if (this.inputRef.current) {
this.makeSomeApiRequest(this.inputRef.current.value);
}
}
render() {
return (
<div>
<input ref={this.inputRef} />
<button onClick={this.handleSubmit}>Отправить</button>
</div>
);
}
}
import React, { Component, createRef } from 'react';
class UncontrolledForm extends Component {
inputRef = createRef<HTMLInputElement>()
handleSubmit = () => {
if (this.inputRef.current) {
this.makeSomeApiRequest(this.inputRef.current.value);
}
}
render() {
return (
<div>
<input ref={this.inputRef} />
<button onClick={this.handleSubmit}>Отправить</button>
</div>
);
}
}
import React, { Component, createRef } from 'react';
class UncontrolledForm extends Component {
inputRef = createRef<HTMLInputElement>()
handleSubmit = () => {
if (this.inputRef.current) {
this.makeSomeApiRequest(this.inputRef.current.value);
}
}
render() {
return (
<div>
<input ref={this.inputRef} />
<button onClick={this.handleSubmit}>Отправить</button>
</div>
);
}
}
import React, { Component, createRef } from 'react';
class UncontrolledForm extends Component {
inputRef = createRef<HTMLInputElement>()
handleSubmit = () => {
if (this.inputRef.current) {
this.makeSomeApiRequest(this.inputRef.current.value);
}
}
render() {
return (
<div>
<input ref={this.inputRef} />
<button onClick={this.handleSubmit}>Отправить</button>
</div>
);
}
}
React управляет текущим состоянием компонента
import React, { Component, FormEvent } from 'react';
class ControlledForm extends Component<ControlledFormProps, ControlledFormState> {
state: ControlledFormState = { value: '' }
handleChange = (event: FormEvent<HTMLInputElement>) => {
this.setState({ value: event.target.value });
}
handleSubmit = () => this.makeSomeApiRequest(this.state.value)
render() {
return (
<div>
<input value={this.state.value} onChange={this.handleChange} />
<button onClick={this.handleSubmit}>Отправить</button>
</div>
);
}
}
import React, { Component, FormEvent } from 'react';
class ControlledForm extends Component<ControlledFormProps, ControlledFormState> {
state: ControlledFormState = { value: '' }
handleChange = (event: FormEvent<HTMLInputElement>) => {
this.setState({ value: event.target.value });
}
handleSubmit = () => this.makeSomeApiRequest(this.state.value)
render() {
return (
<div>
<input value={this.state.value} onChange={this.handleChange} />
<button onClick={this.handleSubmit}>Отправить</button>
</div>
);
}
}
import React, { Component, FormEvent } from 'react';
class ControlledForm extends Component<ControlledFormProps, ControlledFormState> {
state: ControlledFormState = { value: '' }
handleChange = (event: FormEvent<HTMLInputElement>) => {
this.setState({ value: event.target.value });
}
handleSubmit = () => this.makeSomeApiRequest(this.state.value)
render() {
return (
<div>
<input value={this.state.value} onChange={this.handleChange} />
<button onClick={this.handleSubmit}>Отправить</button>
</div>
);
}
}
$ npm install -g create-react-app
$ create-react-app notes-app --typescript
$ cd notes-app
$ npm run start
Compiled successfully!
You can now view notes-app in the browser.
Local: http://localhost:3000/
Note that the development build is not optimized.
To create a production build, use npm run build.