React

libraries

Библиотеки - это отдельные программы которые подключаются к нашей программе. В тоже время они не являются частью нашей программы. Они могут предоставлять дополнительный функционал.

libraries

Communication

DOM

Изначально HTML документ представлял статическую информацию и не предполагал динамику.

React

npx create-react-app my-app
cd my-app
npm start

Quick Start

React

Для упрощения взаимодействия с DOM деревом в react используется виртуальный DOM.

Виртуальный DOM - это легковесная копия DOM дерева но в js.

При изменении состояния react сравнивает предыдущее состояние с текущим и определяет минимальное возможное количество манипуляйций которые возможно произвести.

JSX

JSX производит «элементы» React. То, как элементы рендерятся в DOM, мы изучим в следующей главе, а ниже мы рассмотрим основы JSX, которые нужно знать начинающему.

JSX

React исходит из принципа, что логика рендеринга неразрывно связана с прочей логикой UI: с тем, как обрабатываются события, как состояние изменяется во времени и как данные готовятся к отображению.

JSX

Вместо того, чтобы искусственно разделить технологии, помещая разметку и логику в разные файлы, React разделяет ответственность с помощью слабо связанных единиц, называемых «компоненты», которые содержат и разметку, и логику.

JSX

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';


const element = <h1>Hello, world!</h1>;

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
   { element },
  </React.StrictMode>
);

JSX

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Vick',
  lastName: 'Vl'
};

const element = (
    <h1>
      Hello, {formatName(user)}!
    </h1>
);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    { element },
  </React.StrictMode>
);

JSX допускает использование любых корректных JavaScript-выражений внутри фигурных скобок.

JSX

После компиляции каждое JSX-выражение становится обычным вызовом JavaScript-функции, результат которого — объект JavaScript.

Attributes in JSX

const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;

Не ставьте кавычек вокруг фигурных скобок, когда используете JavaScript-выражение в значении атрибута. Следует либо применить кавычки (для строковых литералов), либо фигурные скобки (для выражений), но не то и другое вместе.

Rendering

<div id="root"></div>

Допустим, в вашем HTML-файле есть <div>:

Обычно в приложениях, написанных полностью на React, есть только один корневой элемент.

Для рендеринга React-элемента в корневой узел DOM вызовите ReactDOM.render()

const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));

Rendering

На практике большинство React-приложений вызывают ReactDOM.render() только один раз.

Rendering

React обновляет только то что необходимо.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));

function tick() {
    const element = (
        <div>
            <h1>Hello, world!</h1>
            <h2>It is {new Date().toLocaleTimeString()}.</h2>
        </div>
    );

    root.render(
      <React.StrictMode>
      { element },
      </React.StrictMode>
    );
}

setInterval(tick, 1000);

Components

Функциональные компоненты

function Welcome(props) {
  return <h1>Hi, {props.name}</h1>;
}

Классовые компоненты

class Welcome extends React.Component {
  render() {
    return <h1>Hi, {this.props.name}</h1>;
  }
}

Components

Рендеринг компонентов

function Welcome(props) {
  return <h1>Привет, {props.name}</h1>;
}

const element = <Welcome name="Алиса" />;

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    element,
  </React.StrictMode>
);

Когда React встречает подобный элемент, он собирает все JSX-атрибуты и дочерние элементы в один объект и передаёт их нашему компоненту. Этот объект называется «пропсы» (props).

Components

Композиция компонентов

function Welcome(props) {
  return <h1>Привет, {props.name}</h1>;
}

function Main() {
  return (
    <div>
      <Welcome name="Алиса" />
      <Welcome name="Базилио" />
      <Welcome name="Буратино" />
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Main />,
  </React.StrictMode>
);

Components

Композиция компонентов

function Welcome(props) {
    return <h1>Hello, {props.name}</h1>;
}
function UserInfo(props) {
    return (
        <>
            <div>{props.name}</div>
            <div>{props.age}</div>
        </>
    );
}
function Main() {
    return (
        <div>
            <Welcome name="Alisa" /> 
            <UserInfo name="Alisa" age="30"/>
            <Welcome name="Basilio" />
            <Welcome name="Booratino" />
        </div>
    );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
     <Main />,
  </React.StrictMode>
);

Components

Обратите внимание на tag <></>

function UserInfo(props) {
    return (
        <>
            <div>{props.name}</div>
            <div>{props.age}</div>
        </>
    );
}

Функции которые возвращают DOM элементы должны быть обернуты в html tag, но когда бы не хотим дополнительную обертку мы используем пустой tag <></>.

Components

Components

Add classes

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}
<div className="UserInfo">

Components

props

function sum(a, b) {
  return a + b;
}

props можно только читать.

Компонент никогда не должен что-то записывать в свои пропсы — вне зависимости от того, функциональный он или классовый.

Такие функции называют «чистыми», потому что они не меняют свои входные данные и предсказуемо возвращают один и тот же результат для одинаковых аргументов.

Components

React-компоненты обязаны вести себя как чистые функции по отношению к своим пропсам.

Components

Компоненты можем выносить в отдельные файлы.

// ./UserInfo.jsx
function UserInfo(props) {
    return (
        <>
            <div>{props.name}</div>
            <div>{props.age}</div>
        </>
    );
}

Вынесем UserInfo в отдельный файл

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';
import UserInfo from "./UserInfo";

function Welcome(props) {
    return <h1>Привет, {props.name}</h1>;
}

function App() {
    return (
        <div>
            <Welcome name="Алиса" />
            <UserInfo name="Алиса" age="30"/>
        </div>
    );
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
     <App />,
  </React.StrictMode>
);

reportWebVitals();

Components

Состояние приложения state

const root = ReactDOM.createRoot(document.getElementById('root'));

function Clock(props) {
  return (
    <div>
      <h1>Привет, мир!</h1>
      <h2>Сейчас {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}

function tick() {
  root.render(
    <React.StrictMode>
      <Clock date={new Date()} />,
    </React.StrictMode>
  );
}

setInterval(tick, 1000);

Рассмотрим пример с Clock.

Нужно сделать так что бы Clock обновлял себя каждую секунду.

Components

Для этого добавим так называемое «состояние» (state) в компонент Clock.

«Состояние» очень похоже на уже знакомые нам пропсы, отличие в том, что состояние контролируется и доступно только конкретному компоненту.

Components

Добавим методы жизненного цикла.

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
  componentDidMount() {
  }
  componentWillUnmount() {
  }
  render() {
    return (
      <div>
        <h1>Привет, мир!</h1>
        <h2>Сейчас {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

Добавим методы жизненного цикла.

Метод componentDidMount() запускается после того, как компонент отрендерился.

Метод componentWillUnmount() запускается после того, как компонент удяляется.

Components

Добавим методы жизненного цикла.

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
  componentDidMount() {
    this.timerID = setInterval(() => this.tick(), 1000);
  }
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
  tick() {
    this.setState({
      date: new Date()
    });
  }
  render() {
    return (
      <div>
        <h1>Привет, мир!</h1>
        <h2>Сейчас {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Clock />,
);

Добавим методы жизненного цикла.

Components

Не изменяйте состояние на прямую, только через setState.

Обновление может быть асинхронное, в таких случаях в setState передаем функцию.

this.setState((state, props) => ({
  counter: state.counter + props.increment
}));
this.setState((state, props) => {
  return {
      counter: state.counter + props.increment
  }
});

Components

Использование хука состояния.

import React, { useState } from 'react';

function Clock(props) {
    const [date, setDate] = useState('00');
    
    return (
        <div>
            <h1>Привет, мир!</h1>
            <h2>Сейчас {date}.</h2>
        </div>
    );
}

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <React.StrictMode>
    <Clock date={new Date()}/>
  </React.StrictMode>
);

Components

Что такое хук?

Хук — это специальная функция, которая позволяет «подцепиться» к возможностям React. Например, хук useState предоставляет функциональным компонентам доступ к состоянию React

useState — это новый способ использовать те же возможности, что даёт this.state в классах

Components

Хук эффекта даёт вам возможность выполнять побочные эффекты в функциональном компоненте:

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';

function Clock(props) {
    const [date, setDate] = useState(getTime());
    useEffect(() => {
        const timer = setTimeout(() => {
            setDate(new Date());
        }, 1000);

        return () => clearTimeout(timer);
    });
    return (
        <div>
            <h1>Привет, мир!</h1>
            <h2>Сейчас {date}.</h2>
        </div>
    );
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Clock />
);

Components

Правила использования хуков:

  1. Не вызывайте хуки внутри циклов.
  2. Вызывайте только из React функций.

Components

Обработка событий

function sayWelcome(data) {
    console.log('Welcome!', data);
}

function Main() {
    return (
        <div>
            <Welcome name="Alisa" />
            <button onClick={sayWelcome}>Say Welcome!</button>
            <button onClick={sayWelcome.bind(this, 'World')}>Say Welcome2!</button>
            <button onClick={() => sayWelcome("World")}>Say Welcome 3!</button>
            <UserInfo name="Alisa" age="30"/>
        </div>
    );
}

Components

Обработка событий

function Form() {
    function handleSubmit(e) {
        e.preventDefault();
        console.log('Отправлена форма.');
    }

    return (
        <form onSubmit={handleSubmit}>
            <button type="submit">Отправить</button>
        </form>
    );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Form />
  </React.StrictMode>
);

Components

import React, { useState } from 'react'
const App = () => {
  const [formValues, setFormValues] = useState([{ name: "" }])
  let removeFormFields = (i) => {
    let newFormValues = [...formValues];
    newFormValues.splice(i, 1);
    setFormValues(newFormValues)
  }
  let handleChange = (index, event) => {
    let newFormValues = [...formValues];
    newFormValues[index][event.target.name] = event.target.value;
    setFormValues(newFormValues);}
  let handleSubmit = (event) => { event.preventDefault(); }
  return (
      <form  onSubmit={handleSubmit}>
        {formValues.map((element, index) => (
            <div className="form-inline" key={index}>
              <label>Name</label>
              <input type="text" name="name" value={element.name || ""} onChange={e => handleChange(index, e)} />
              {
                index || index === 0 ?
                    <button type="button"  className="button remove" onClick={() => removeFormFields(index)}>Remove</button>
                    : null
              }
            </div>
        ))}
        <div className="button-section">
          <button className="button submit" type="submit">Submit</button>
        </div>
      </form>
  )
}

Components

Form

ReactDOM.render(
    <App />,
    document.getElementById('root')
);

React

By Oleg Rovenskyi