Вводимо react.dev

16 березня 2023 року від Дена Абрамова та Рейчел Наборс


Сьогодні ми з радістю запускаємо react.dev, новий дім для React та його документації. У цьому пості ми хотіли б провести для вас екскурсію новим сайтом.


tl;dr

  • Новий сайт React (react.dev) навчає сучасному React з функціональними компонентами та хуками.
  • Ми додали діаграми, ілюстрації, задачі та понад 600 нових інтерактивних прикладів.
  • Попередній сайт документації React переміщено на legacy.reactjs.org.

Новий сайт, новий домен, нова домашня сторінка

Спочатку трохи про домашнє господарство.

Щоб відсвяткувати запуск нової документації і, що більш важливо, чітко розділити старий і новий контент, ми переїхали на коротший домен react.dev. Старий домен reactjs.org тепер буде перенаправляти сюди.

Старі документи React тепер заархівовано за адресою legacy.reactjs.org. Всі існуючі посилання на старий контент будуть автоматично перенаправлені туди, щоб уникнути "розриву мережі", але старий сайт більше не отримуватиме оновлень.

Вірте чи ні, але React скоро виповниться десять років. У роках JavaScript це як ціле століття! Ми оновили домашню сторінку React, щоб показати, чому ми вважаємо React чудовим способом створення користувацьких інтерфейсів сьогодні, а також оновили посібники для початківців, щоб більше згадати про сучасні фреймворки на основі React.

Якщо ви ще не бачили нову домашню сторінку, перегляньте її!

Йдемо ва-банк на сучасному React з хуками

Коли ми випустили хуки React у 2018 році, документація до хуків припускала, що читач знайомий з класовими компонентами. Це допомогло спільноті прийняти хуки дуже швидко, але через деякий час старі документи не змогли обслуговувати нових читачів. Новим читачам довелося вивчати React двічі: спочатку з класовими компонентами, а потім знову з хуками.

Нові документи навчають React з хуками з самого початку. Документи розділено на два основні розділи:

Давайте детальніше розглянемо, що можна знайти у кожному розділі.

Існує ще декілька рідкісних випадків використання компонентів класів, для яких ще не існує еквівалента на основі хуків. Компоненти класів і надалі підтримуються і задокументовані у розділі Legacy API на новому сайті.

Швидкий старт

Розділ "Навчання" починається зі сторінки Швидкий старт. Це короткий вступний тур по React. Вона знайомить з синтаксисом таких понять, як компоненти, пропси та стан, але не дуже детально описує, як їх використовувати.

Якщо вам подобається вчитися на практиці, рекомендуємо ознайомитися з Підручником з гри у хрестики-нулики далі. Він допоможе вам створити невелику гру за допомогою React, водночас навчаючи навичкам, які ви будете використовувати щодня. Ось що ви побудуєте:

import { useState } from 'react';

function Square({ value, onSquareClick }) {
  return (
    <button className="square" onClick={onSquareClick}>
      {value}
    </button>
  );
}

function Board({ xIsNext, squares, onPlay }) {
  function handleClick(i) {
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = 'X';
    } else {
      nextSquares[i] = 'O';
    }
    onPlay(nextSquares);
  }

  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = 'Winner: ' + winner;
  } else {
    status = 'Next player: ' + (xIsNext ? 'X' : 'O');
  }

  return (
    <>
      <div className="status">{status}</div>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />
        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />
        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />
        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />
        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />
        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />
      </div>
    </>
  );
}

export default function Game() {
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const [currentMove, setCurrentMove] = useState(0);
  const xIsNext = currentMove % 2 === 0;
  const currentSquares = history[currentMove];

  function handlePlay(nextSquares) {
    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
    setHistory(nextHistory);
    setCurrentMove(nextHistory.length - 1);
  }

  function jumpTo(nextMove) {
    setCurrentMove(nextMove);
  }

  const moves = history.map((squares, move) => {
    let description;
    if (move > 0) {
      description = 'Go to move #' + move;
    } else {
      description = 'Go to game start';
    }
    return (
      <li key={move}>
        <button onClick={() => jumpTo(move)}>{description}</button>
      </li>
    );
  });

  return (
    <div className="game">
      <div className="game-board">
        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
      </div>
      <div className="game-info">
        <ol>{moves}</ol>
      </div>
    </div>
  );
}

function calculateWinner(squares) {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}
* {
  box-sizing: border-box;
}

body {
  font-family: sans-serif;
  margin: 20px;
  padding: 0;
}

.square {
  background: #fff;
  border: 1px solid #999;
  float: left;
  font-size: 24px;
  font-weight: bold;
  line-height: 34px;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  text-align: center;
  width: 34px;
}

.board-row:after {
  clear: both;
  content: '';
  display: table;
}

.status {
  margin-bottom: 10px;
}
.game {
  display: flex;
  flex-direction: row;
}

.game-info {
  margin-left: 20px;
}

Ми також хотіли б виділити Думати на React - це підручник, який зробив React "метчем" для багатьох з нас. Ми оновили обидва ці класичні підручники для використання функціональних компонентів та хуків, тож вони тепер як новенькі.

Приклад вище - це пісочниця sandbox. Ми додали багато пісочниць - понад 600! - по всьому сайту. Ви можете редагувати будь-яку пісочницю або натиснути "Fork" у верхньому правому куті, щоб відкрити її в окремій вкладці. Пісочниці дозволяють швидко погратися з React API, дослідити свої ідеї та перевірити своє розуміння.

Вивчайте React крок за кроком

Ми б хотіли, щоб всі у світі мав рівну можливість вивчати React самостійно та безкоштовно.

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

Наступні два розділи є більш складними і дадуть вам глибше розуміння складніших частин:

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

Кожна глава складається з декількох пов'язаних сторінок. Більшість з цих сторінок навчають певній навичці або техніці, наприклад, Запис розмітки за допомогою JSX, Оновлення об'єктів у стані, або Поділ стану між компонентами. Деякі сторінки присвячено поясненню ідеї, наприклад, Рендеринг та фіксація, або Стан як знімок. І є декілька, наприклад Вам може не знадобитися ефект, у яких ми ділимося нашими порадами на основі того, чого ми навчилися за ці роки.

Вам не обов'язково читати ці розділи послідовно. Хто має на це час?! Але ви можете це зробити. Сторінки розділу "Навчання" ґрунтуються лише на поняттях, представлених на попередніх сторінках. Якщо ви хочете читати його як книжку - вперед!

Перевірте своє розуміння за допомогою завдань

Більшість сторінок розділу Навчання закінчуються кількома завданнями для перевірки вашого розуміння. Наприклад, ось кілька завдань зі сторінки про Умовний рендеринг.

Вам не потрібно вирішувати їх прямо зараз! Якщо тільки ви дійсно цього не хочете.

Показувати піктограму для незавершених елементів з ? :

Використовуйте умовний оператор (cond ? a : b) для виведення ❌, якщо isPacked не є істинним .

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✔'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}
function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked ? '✔' : '❌'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Показати важливість елемента за допомогою &&

У цьому прикладі кожен Item отримує числову важливість пропу . Використовуйте оператор && для виділення "(Важливість: X)" курсивом, але лише для елементів, які мають ненульову важливість. Ваш список елементів має виглядати так:

  • Скафандр (Важливість: 9)
  • Шолом із золотим листям
  • Фотографія Там(Важливість: 6)

Не забудьте додати пробіл між двома мітками!

function Item({ name, importance }) {
  return (
    <li className="item">
      {name}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          importance={9} 
          name="Space suit" 
        />
        <Item 
          importance={0} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          importance={6} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Це має спрацювати:

function Item({ name, importance }) {
  return (
    <li className="item">
      {name}
      {importance > 0 && ' '}
      {importance > 0 &&
        <i>(Importance: {importance})</i>
      }
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          importance={9} 
          name="Space suit" 
        />
        <Item 
          importance={0} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          importance={6} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Зверніть увагу, що ви повинні написати importance > 0 && ..., а не importance && ..., щоб якщо значення дорівнює 0, 0 не виводилося як результат!

У цьому рішенні використано дві окремі умови для вставки пробілу між назвою та міткою важливості. Крім того, ви можете використати фрагмент з пробілом на початку: importance > 0 && <> <i>...</i></> або додати пробіл безпосередньо всередині <i>: importance > 0 && <i> ...</i>.

Помітьте кнопку "Показати рішення" у лівому нижньому кутку. Це зручно, якщо ви хочете перевірити себе!

Побудова інтуїції з діаграмами та ілюстраціями

Коли ми не могли з'ясувати, як пояснити щось лише за допомогою коду та слів, ми додали діаграми, які допомагають додати деяку інтуїтивність. Наприклад, ось одна з діаграм з Збереження та скидання стану:

Diagram with three sections, with an arrow transitioning each section in between. The first section contains a React component labeled 'div' with a single child labeled 'section', which has a single child labeled 'Counter' containing a state bubble labeled 'count' with value 3. The middle section has the same 'div' parent, but the child components have now been deleted, indicated by a yellow 'proof' image. The third section has the same 'div' parent again, now with a new child labeled 'div', highlighted in yellow, also with a new child labeled 'Counter' containing a state bubble labeled 'count' with value 0, all highlighted in yellow.

Коли section змінюється на div, section видаляється і додається новий div

Ви також побачите деякі ілюстрації у документації - ось одна з них, де браузер відображає екран:

Ми підтвердили у постачальників браузерів, що це зображення є на 100% науково точним.

Нове, детальне посилання на API

У Довіднику API кожен React API тепер має окрему сторінку. Це включає всі види API:

  • Вбудовані хуки на кшталт useState.
  • Вбудовані компоненти, такі як <Suspense>.
  • Вбудовані компоненти браузера, такі як <input>.
  • Фреймворк-орієнтовані API, такі як renderToPipeableStream.
  • Інші React API, такі як memo.

Ви помітите, що кожна сторінка API розбита принаймні на два сегменти: Посилання та Використання.

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

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

Лічильник (число)

У цьому прикладі змінна стану count містить число. Натискання кнопки збільшує його.

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      You pressed me {count} times
    </button>
  );
}

Текстове поле (рядок)

У цьому прикладі змінна стану text містить рядок. Коли ви вводите текст, handleChange зчитує останнє вхідне значення з DOM-елемента введення браузера і викликає setText для оновлення стану. Це дозволяє відобразити поточний текст нижче.

import { useState } from 'react';

export default function MyInput() {
  const [text, setText] = useState('hello');

  function handleChange(e) {
    setText(e.target.value);
  }

  return (
    <>
      <input value={text} onChange={handleChange} />
      <p>You typed: {text}</p>
      <button onClick={() => setText('hello')}>
        Reset
      </button>
    </>
  );
}

Прапорець (boolean)

У цьому прикладі змінна стану liked містить логічне значення. Коли ви натискаєте на вхід, setLiked оновлює змінну стану liked тим, чи встановлено прапорець у вікні браузера. Змінна liked використовується для відображення тексту під прапорцем.

import { useState } from 'react';

export default function MyCheckbox() {
  const [liked, setLiked] = useState(true);

  function handleChange(e) {
    setLiked(e.target.checked);
  }

  return (
    <>
      <label>
        <input
          type="checkbox"
          checked={liked}
          onChange={handleChange}
        />
        I liked this
      </label>
      <p>You {liked ? 'liked' : 'did not like'} this.</p>
    </>
  );
}

Форма (дві змінні)

В одному компоненті можна оголосити більше однієї змінної стану. Кожна змінна стану є повністю незалежною.

import { useState } from 'react';

export default function Form() {
  const [name, setName] = useState('Taylor');
  const [age, setAge] = useState(42);

  return (
    <>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <button onClick={() => setAge(age + 1)}>
        Increment age
      </button>
      <p>Hello, {name}. You are {age}.</p>
    </>
  );
}
button { display: block; margin-top: 10px; }

Деякі сторінки API також містять Вирішення проблем (для типових проблем) та Альтернативи (для застарілих API).

Ми сподіваємося, що такий підхід зробить посилання на API корисним не лише як спосіб пошуку аргументу, але й як спосіб побачити всі різні речі, які можна зробити за допомогою будь-якого API, і як він пов'язаний з іншими.

Що далі?

На цьому наша маленька екскурсія завершується! Ознайомтеся з новим сайтом, подивіться, що вам подобається, а що ні, і залишайте відгуки в анонімному опитуванні або на нашому трекері релізів.

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

Ми почули багато ваших побажань щодо розширення контенту та функціональності сайту, наприклад:

  • Надання версії TypeScript для всіх прикладів;
  • Створення оновлених посібників з продуктивності, тестування та доступності;
  • Документування компонентів React Server незалежно від фреймворків, які їх підтримують;
  • Працюємо з міжнародною спільнотою над перекладом нової документації;
  • Додавання відсутніх функцій до нового веб-сайту (наприклад, RSS для цього блогу).

Тепер, коли react.dev вийшов, ми зможемо змістити фокус з "наздоганяння" сторонніх освітніх ресурсів React на додавання нової інформації та подальше вдосконалення нашого нового сайту.

Ми вважаємо, що зараз найкращий час для вивчення React.

Хто над цим працював?

У команді React Рейчел Наборс керувала проектом (і надала ілюстрації), а Ден Абрамов розробив навчальну програму. Вони також є співавторами більшості матеріалів.

Звісно, жоден великий проект не створюється в ізоляції. Ми маємо багатьом людям подякувати!

Сильвія Варгас переробила наші приклади, щоб вийти за рамки "foo/bar/baz" та кошенят і показати вчених, митців та міста з усього світу. Меґі Епплтон перетворила наші малюнки на чітку систему діаграм.

Завдяки David McCabe, Sophie Alpert, Rick Hanlon, Ендрю Кларку та Метту Керролу за додатковий внесок у написання статті. Ми також хочемо подякувати Наталії Теплухіній та Себастьяну Маркбоге за їхні ідеї та відгуки.

Дякуємо Дену Лебовіцу за дизайн сайту та Развану Градінару за дизайн пісочниці.

На фронті розробки дякуємо Джареду Палмеруr за розробку прототипу. Дякуємо Дейну Гранту та Дастіну Гудману з ThisDotLabs за підтримку у розробці інтерфейсу. Дякуємо Іву ван Хоорну, Алексу Молдовану, Ясперу де Моору, та Данилі Возниця з CodeSandbox за їх роботу з інтеграцією у пісочниці. Дякуємо Ріку Ханлону за точкову розробку та роботу над дизайном, покращенням наших кольорів та дрібних деталей. Дякуємо Харішу Кумару та Луні Руанn за додавання нових функцій на сайт та допомогу в його підтримці.

Величезна подяка людям, які добровільно присвятили свій час участі у програмах альфа- та бета-тестування. Ваш ентузіазм і безцінні відгуки допомогли нам сформувати ці документи. Особлива подяка нашому бета-тестеру, Деббі О'Брайен, яка розповіла про свій досвід використання React-документів на React Conf 2021.

І наостанок, дякуємо спільноті React за натхнення, яке стоїть за цією роботою. Саме завдяки вам ми це робимо, і ми сподіваємося, що нові документи допоможуть вам використовувати React для створення будь-якого бажаного вами користувацького інтерфейсу.