JavaScript Crash Course

React

12

Зміст:

  1. Hooks

  2. State

  3. Lifecycle

  4. Рендеринг з умовами

  5. Опрацювання подій

  6. Робота з формами

  7. Робота з HTTP запитами

State

React State

      State може містити будь-яке значення JavaScript, включаючи об’єкти. Так, як state - це read-only об'єкт ми не повинні безпосередньо змінювати значення, які збегіраються в стані React.

     Саме тому потрібно використовувати React.useState() hook.

 

const [x, setX] = useState(0);

React State та початковий стан

import { useState } from 'react';

function MyComponent() {
  const [age, setAge] = useState(28);
  const [name, setName] = useState('Taylor');
  const [todos, setTodos] = useState(() => createTodos());
  // ...
import { useState } from 'react';

export default function MovingDot() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  return (
    <div
      onPointerMove={e => {
        position.x = e.clientX;
        position.y = e.clientY;
      }}
      style={{
        position: 'relative',
        width: '100vw',
        height: '100vh',
      }}>
      <div style={{
        position: 'absolute',
        backgroundColor: 'red',
        borderRadius: '50%',
        transform: `translate(${position.x}px, ${position.y}px)`,
        left: -10,
        top: -10,
        width: 20,
        height: 20,
      }} />
    </div>
  );
}

Життєвий цикл компонентів

Lifecycle

Життєвий цикл компонента є однією з найважливіших концепцій React.

Методи життєвого циклу дають нам багато можливостей контролювати оновлення програм тощо.

Mounting

Updating

Unmounting

Методи класового компоненту

     React API надає кілька методів життєвого циклу для компонентів класу. Найважливішими з них є componentDidMount, componentDidUpdate, componentWillUnmount.

Монтування та componentDidMount

Послідовність відпрацювання:

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

Оновлення з використанням componentDidUpdate

Оновлення викликано props або змінами стану. Це відбувається в такому порядку:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

Демонтування компонента

componentWillUnmount() викликається безпосередньо перед демонтуванням компонента. Цей метод використовують, щоб очистити необхідні таймери та підписки або скасувати мережеві запити.

Інші методи життєвого циклу класових компонентів

 getDerivedStateFromProps(props, state)

componentDidCatch(error, info)

forceUpdate(callback)

componentWillReceiveProps(nextProps)

componentWillReceiveProps(nextProps)

getSnapshotBeforeUpdate(prevProps, prevState)

componentWillReceiveProps(nextProps)

useState()

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>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

React Hooks Lifecycle

Життєвий цикл функціональних компонентів

Монтування компонента

  • Run LayoutEffects
  • Browser is painting the screen
  • Run Effects

Оновлення компонента

  • Render
  • React updates DOM
  • Cleanup LayoutEffects
  • Run LayoutEffects
  • Browser is painting the screen
  • Cleanup Effects
  • Run Effects

Демонтування компонента

  • Clean up LayoutEffects
  • Clean up Effects

Використання useEffect hook

Використання useEffect hook

/* Run only on mounting */
React.useEffect(() => {
      // function body
}, []);

/* Run on props/state update */
React.useEffect(() => {
    // it will be called only when "exampleProp" changes
}, [exampleProp]);
React.useEffect(() => {
    // it will be called for every component update
});

/* Unmount */
React.useEffect(() => {
    return () => {
        // unsubscribe
    }
}, ]);
React.useEffect(() => {
    return () => {
        // unsubscribe
        // this cleanup function will be triggered on each dependency change
    }
}, [dependency]);

Hooks

Built-in React Hooks

Hooks дозволяють використовувати різні функції React із ваших компонентів. Ви можете використовувати вбудовані хуки або комбінувати їх для створення власних. На цій сторінці перераховані всі вбудовані хуки в React.

function ImageGallery() {
  const [index, setIndex] = useState(0);
  // ...
 

State Hooks

    Щоб додати стан до компонента, скористайтеся одним із цих хуків:

  • useState оголошує змінну стану, яку можна оновити безпосередньо.
  • useReducer оголошує змінну стану з логікою оновлення всередині функції редюсера.
function ImageGallery() {
  const [index, setIndex] = useState(0);
  // ...
 

Context Hooks

        Контекст дозволяє компоненту отримувати інформацію від віддалених батьків, не передаючи її як атрибути. Наприклад, компонент верхнього рівня вашої програми може передавати поточну тему інтерфейсу користувача всім компонентам нижче, незалежно від глибини.

function Button() {
  const theme = useContext(ThemeContext);
  // ...
  • useContext читає контекст і підписується на нього.

Ref Hooks

Посилання дозволяють компоненту зберігати деяку інформацію, яка не використовується для відтворення, як-от вузол DOM або ідентифікатор часу очікування. На відміну від стану, оновлення посилання не відтворює повторно ваш компонент. Refs — це «аварійний люк» із парадигми React. Вони корисні, коли вам потрібно працювати з системами, не пов’язаними з React, такими як вбудовані API браузера.

  • useRef оголошує посилання. У ньому можна зберігати будь-яке значення, але найчастіше воно використовується для зберігання вузла DOM
  • useImperativeHandle (рідко використовується)
function Form() {
  const inputRef = useRef(null);
  // ...

Effect Hooks

    Ефекти дозволяють компоненту підключатися до зовнішніх систем і синхронізуватися з ними. Це включає роботу з мережею, DOM браузера, анімаціями, віджетами, написаними з використанням іншої бібліотеки інтерфейсу користувача, та іншим кодом, не пов’язаним з React.

function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = createConnection(roomId);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);
  // ...
  • useEffect підключає компонент до зовнішньої системи

Performance Hooks

Поширений спосіб оптимізації продуктивності повторного рендерингу – це пропуск непотрібної роботи. Наприклад, React може повторно використовувати кешований обчислення або пропустити повторну візуалізацію, якщо дані не змінилися з часу попередньої візуалізації.

Щоб пропустити обчислення та непотрібне повторне рендеринг, скористайтеся одним із цих хуків:

  • useMemo дозволяє кешувати результат дорогого обчислення
  • useCallback дозволяє кешувати визначення функції перед передачею її оптимізованому
function TodoList({ todos, tab, theme }) {
  const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
  // ...
}

Performance Hooks

import { useCallback } from 'react';

export default function ProductPage({ productId, referrer, theme }) {
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]);
  

 Власні Hooks

import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";

const Home = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/todos")
      .then((res) => res.json())
      .then((data) => setData(data));
 }, []);

  return (
    <>
      {data &&
        data.map((item) => {
          return <p key={item.id}>{item.title}</p>;
        })}
    </>
  );
};

Логіку пов'язану із запитами можна перевикористати

 Власні Hooks

import { useState, useEffect } from "react";

// custom hook
const useFetch = (url) => {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then((data) => setData(data));
  }, [url]);

  return [data];
};

// component
const Home = () => {
  const [data] = useFetch("https://jsonplaceholder.typicode.com/todos");
  return (
    <>
      {data &&
        data.map((item) => {
          return <p key={item.id}>{item.title}</p>;
        })}
    </>
  );
};

Рендеринг з умовами

Відображення компонентів за умовами

       Досить часто потрібно додавати умову, коли потрібно відображувати компонент. Наприклад під час завантаження даних, бажано відображувати "лоадер": 

 

1

2

3

4

5

6

Відображення "лоадера"

import { useState, useEffect } from 'react';
import useFetch from '../utils/useFetch';
import Loader from './Loader'; // component with a loader

const Home = () => {
  const [data] = useFetch("https://jsonplaceholder.typicode.com/todos");

  return (
    <>
      {!data
       	? <Loader />
        : data.map((item) => {
          return <p key={item.id}>{item.title}</p>;
        })}
    </>
  );
};

Приховування компоненту, якщо немає прав

import { useState, useEffect } from 'react';
import useFetch from '../utils/useFetch';
import Account from './Account';
import EditAccountButton from './EditAccountButton';

const Home = props => {
  const hasPermissions = props.roles.includes(role => role === 'editAccount');
  
  return (
    <div>
      <Account />
      { hasPermission && <EditAccountButton />}      
    </div>
  );
};

Component

import React from 'react';

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

export default Welcome;

     Компоненти дозволяють розділити інтерфейс на незалежні частини, які можна багаторазово використовувати, і розглядати кожну частину окремо. 

     Концептуально компоненти схожі на функції JavaScript. Вони приймають довільні вхідні дані (так звані «props») і повертають елементи React, які описують те, що має з’явитися на екрані.

Опрацювання подій 

Події в React

<button onClick={activateLasers}>
  Activate Lasers
</button>

Обробка подій з елементами React дуже схожа на обробку подій елементів DOM. Є деякі синтаксичні відмінності:

  • Події React називаються в верблюжому регістрі, а не в нижньому регістрі.
  • За допомогою JSX ви передаєте функцію як обробник події, а не рядок.

Наприклад, HTML:

і те, як потрібно в React:

<button onclick="activateLasers()">
  Activate Lasers
</button>

Події в React

function Form() {
  function handleSubmit(e) {
    e.preventDefault();
    console.log('You clicked submit.');
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Submit</button>
    </form>
  );
}

Події в React: класовий компонент

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

Робота з формами

Робота з формами

import { useState } from 'react';

function MyForm() {
  const [name, setName] = useState("");

  const handleSubmit = (event) => {
    event.preventDefault();
    alert(`The name you entered was: ${name}`)
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>Enter your name:
        <input 
          type="text" 
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
      </label>
      <input type="submit" />
    </form>
  )
}

Робота з формами: multiple inputs

import { useState } from 'react';
import ReactDOM from 'react-dom/client';

function MyForm() {
  const [inputs, setInputs] = useState({});

  const handleChange = (event) => {
    const name = event.target.name;
    const value = event.target.value;
    setInputs(values => ({...values, [name]: value}))
  }

  const handleSubmit = (event) => {
    event.preventDefault();
    alert(inputs);
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>Enter your name:
      <input 
        type="text" 
        name="username" 
        value={inputs.username || ""} 
        onChange={handleChange}
      />
      </label>
      <label>Enter your age:
        <input 
          type="number" 
          name="age" 
          value={inputs.age || ""} 
          onChange={handleChange}
        />
        </label>
        <input type="submit" />
    </form>
  )
}

Робота з формами: класові компоненти

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'Please write an essay about your favorite DOM element.'
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleChange(event) {
    this.setState({value: event.target.value});
  }
  handleSubmit(event) {
    alert('An essay was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Essay:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

npm

npm install

npm add [package] --save-dev  # dev dependencies
npm add [package] # dependencies

npm — це менеджер пакетів для мови програмування JavaScript, який підтримується npm, Inc. npm — це менеджер пакетів за замовчуванням для середовища виконання JavaScript Node.js.

Він складається з командного рядка клієнта, який також називається npm, і онлайнової бази даних загальнодоступних і платних приватних пакетів, яка називається реєстром npm.

yarn/npm та package.json

npm run start

Для того, щоб запустити застосунок необхідно використати наступну npm команду:

yarn start

Або використати yarn:

yarn/npm та package.json

{
  "name": "first-react-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Всі доступні команди, а також набір залежностей описаний в package.json

Всі встановлені залежності будуть доступні в директорії node_modules

yarn/npm та package.json

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },

Команда build дозволяє отримати фінальний результат, який можна розмістити на хостингу або інших ресурсах

yarn build

Робота з HTTP запитами

import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";

const Home = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/todos")
      .then((res) => res.json())
      .then((data) => setData(data));
 }, []);

  return (
    <>
      {data &&
        data.map((item) => {
          return <p key={item.id}>{item.title}</p>;
        })}
    </>
  );
};

useEffect та useState

Q & A

_14 JavaScript Crash Course: React

By Inna Ivashchuk

_14 JavaScript Crash Course: React

  • 294