Даданне інтэрактыўнасці

Некаторыя рэчы на экране абнаўляюцца ў адказ на ўвод карыстальніка. Напрыклад, націснуўшы любы відарыс у галерэі відарысаў вы пераключаеце актыўны відарыс. У React даныя, якія змяняюцца з часам, называюцца станам. Вы можаце дадаць стан любому кампаненту і абнавіць яго калі вам трэба. У гэтай главе вы даведаецеся, як пісаць кампаненты, якія апрацоўваюць узаемадзеянне, абнаўляюць свой стан і змяняюць свой выгляд з цягам часу.

Рэагаванне на падзеі

React дазваляе дадаваць апрацоўшчыкі падзей у ваш JSX. Апрацоўшчыкі падзей — гэта вашыя ўласныя функцыі, якія будуць запускацца ў адказ на ўзаемадзеянне карыстальніка, напрыклад, націсканне кнопак, навядзенне курсора на элементы, атрыманне фокусу на ўводах формы і гэтак далей.

Убудаваныя кампаненты, такія як <button>, падтрымліваюць толькі ўбудаваныя падзеі браўзера, такія як onClick. Аднак, вы таксама можаце ствараць свае ўласныя кампаненты і даваць іхнім апрацоўшчыкам падзей любыя назвы, спецыфічныя для праграмы, на ваш густ.

export default function App() {
  return (
    <Toolbar
      onPlayMovie={() => alert('Ідзе прайграванне!')}
      onUploadImage={() => alert('Ідзе запампоўванне!')}
    />
  );
}

function Toolbar({ onPlayMovie, onUploadImage }) {
  return (
    <div>
      <Button onClick={onPlayMovie}>
        Прайграць фільм
      </Button>
      <Button onClick={onUploadImage}>
        Запампаваць відарыс
      </Button>
    </div>
  );
}

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

Ready to learn this topic?

Звярніцеся да старонкі «Рэагаванне на падзеі» каб даведацца як дадаваць апрацоўшчыкі падзей.

Read More

Стан: памяць кампанента

У выніку ўзаемадзеяння кампаненты часта павінны змяніць тое, што знаходзіцца на экране. Увод у форму павінен абнавіць поле ўводу, націсканне кнопкі «далей» на каруселі відарысаў павінна змяніць відарыс, які адлюстроўваецца, націсканне кнопкі «купіць» кладзе прадукт у кошык. Кампанентам трэба «запамінаць» рознае: бягучае ўваходнае значэнне, бягучы відарыс, кошык для пакупак. У React такая спецыфічная для кампанентаў памяць называецца стан.

Вы можаце дадаць стан да кампанента з дапамогай хука useState. Хукі — гэта такія спецыяльныя функцыі, што дазваляюць вашым кампанентам выкарыстоўваць функцыі React (стан — адна з такіх функцый). Хук useState дазваляе аб’явіць пераменную стану. Ён прымае пачатковае значэнне стану і вяртае пару значэнняў: бягучае значэнне стану і функцыю для задавання стану, якая дазваляе абнаўляць яго значэнне.

const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);

Вось як галерэя відарысаў выкарыстоўвае і абнаўляе стан пры націсканні:

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);
  const hasNext = index < sculptureList.length - 1;

  function handleNextClick() {
    if (hasNext) {
      setIndex(index + 1);
    } else {
      setIndex(0);
    }
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleNextClick}>
        Далей
      </button>
      <h2>
        <i>«{sculpture.name}» </i>
        {sculpture.artist}
      </h2>
      <h3>
        ({index + 1} з {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        {showMore ? 'Схаваць' : 'Паказаць'} падрабязнасці
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img
        src={sculpture.url}
        alt={sculpture.alt}
      />
    </>
  );
}

Ready to learn this topic?

Звярніцеся да старонкі «Стан: памяць кампанента» каб даведацца як запамінаць значэнне і абнаўляць яго пры ўзаемадзеянні.

Read More

Рэндэрынг і фіксацыя

Перш чым вашыя кампаненты будуць адлюстраваны на экране, React мусіць адрэндэрыць іх. Разуменне этапаў гэтага працэсу дапаможа вам зразумець, як выконваецца ваш код, і растлумачыць яго паводзіны.

Уявіце, што вашы кампаненты — гэта кухары на кухні, якія збіраюць смачныя стравы з інгрэдыентаў. У гэтым сцэнарыі React — гэта афіцыянт, які запісвае запыты ад кліентаў і пасля прыносіць ім іх заказы. Гэты працэс запыту і «падачы» карыстальніцкага інтэрфейсу складаецца з трох этапаў:

  1. Ініцыяванне рэндэру (дастаўка заказу на кухню)
  2. Рэндэрынг кампанента (гатаванне заказу на кухні)
  3. Фіксацыя ў DOM (падаванне заказу на стол)
  1. React выконвае ролю афіцыянта ў рэстаране, які атрымлівае заказы ад карыстальніка і перадае іх кампаненту Kitchen.
    Ініцыяванне
  2. Шэф-повар дае React свежы кампанент Card.
    Рэндэрынг
  3. React дастаўляе кампанент Card карыстальніку на яго столік.
    Фіксацыя

Illustrated by Rachel Lee Nabors

Ready to learn this topic?

Звярніцеся да старонкі «Адлюстраванне і фіксацыя» каб даведацца як працуе жыццёвы цыкл абнаўлення карыстальніцкага інтэрфейсу.

Read More

Стан як хуткі здымак

У адрозненне ад звычайных пераменных у JavaScript, стан у React паводзіць сябе больш як хуткі здымак (snapshot). Заданне новага значэння стану не змяняе саму пераменную стану, якая ў вас ужо ёсць, а запускае паўторны рэндэрынг. Гэта можа здзівіць спачатку!

console.log(count); // 0
setCount(count + 1); // Запытвае паўторны рэндэрынг са значэннем 1
console.log(count); // Усё яшчэ 0!

Такія паводзіны дапамагаюць пазбегнуць няўлоўных памылак. Вось невялікая праграма-чат. Паспрабуйце здагадацца, што адбудзецца, калі вы спачатку націснеце «Адправіць», а затым зменіце атрымальніка на Боба. Чыё імя з’явіцца ў акне alert праз пяць секунд?

import { useState } from 'react';

export default function Form() {
  const [to, setTo] = useState('Аліса');
  const [message, setMessage] = useState('Вітаю');

  function handleSubmit(e) {
    e.preventDefault();
    setTimeout(() => {
      alert(`Вы сказалі ${to}: ${message}`);
    }, 5000);
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Для:{' '}
        <select
          value={to}
          onChange={e => setTo(e.target.value)}>
          <option value="Аліса">Аліса</option>
          <option value="Боб">Боб</option>
        </select>
      </label>
      <textarea
        placeholder="Паведамленне"
        value={message}
        onChange={e => setMessage(e.target.value)}
      />
      <button type="submit">Адправіць</button>
    </form>
  );
}

Ready to learn this topic?

Даведайцеся ў артыкуле «Стан як хуткі здымак» аб тым, чаму стан здаецца «фіксаваным» і нязменным у апрацоўшчыках падзей.

Read More

Чарга абнаўленняў стану

Гэты кампанент не правільна працуе: націсканне кнопкі «+3» павялічвае бал толькі адзін раз.

import { useState } from 'react';

export default function Counter() {
  const [score, setScore] = useState(0);

  function increment() {
    setScore(score + 1);
  }

  return (
    <>
      <button onClick={() => increment()}>+1</button>
      <button onClick={() => {
        increment();
        increment();
        increment();
      }}>+3</button>
      <h1>Балы: {score}</h1>
    </>
  )
}

Артыкул «Стан як хуткі здымак» тлумачыць, чаму так адбываецца. Задаванне новага значэння стану запытвае новы рэндэрынг, але не змяняе існуючае значэнне стану ва ўжо запушчаным кодзе. Таму пераменная score працягвае быць роўнай 0 адразу пасля таго, як вы выклікаеце setScore(score + 1).

console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0

Вы можаце выправіць гэта, перадаўшы функцыю абнаўлення пры ўсталяванні стану. Звярніце ўвагу, як замена setScore(score + 1) на setScore(s => s + 1) выпраўляе паводзіны кнопкі «+3». Гэта дазваляе вам ставіць у чаргу некалькі абнаўленняў стану.

import { useState } from 'react';

export default function Counter() {
  const [score, setScore] = useState(0);

  function increment() {
    setScore(s => s + 1);
  }

  return (
    <>
      <button onClick={() => increment()}>+1</button>
      <button onClick={() => {
        increment();
        increment();
        increment();
      }}>+3</button>
      <h1>Балы: {score}</h1>
    </>
  )
}

Ready to learn this topic?

Звярніцеся да старонкі «Чарга абнаўленняў стану» каб даведацца як паставіць у чаргу некалькі абнаўленняў стану.

Read More

Абнаўленне аб’ектаў у стане

Стан можа ўтрымліваць любы тып значэння JavaScript, у тым ліку аб’екты. Але вы не мусіце змяняць напрамую аб’екты і масівы, якія вы захоўваеце ў стане React. Замест гэтага, калі вы хочаце абнавіць аб’ект і масіў, вам трэба стварыць новы (або зрабіць копію існуючага), а затым абнавіць стан так, каб ён пачаў выкарыстоўваць гэтую копію.

Звычайна для капіравання аб’ектаў і масіваў, якія вы хочаце змяніць, вы будзеце выкарыстоўваць сінтаксіс разгортвання (spread syntax) .... Напрыклад, абнаўленне ўкладзенага аб’екта можа выглядаць так:

import { useState } from 'react';

export default function Form() {
  const [person, setPerson] = useState({
    name: 'Нікі дэ Сен-Фаль',
    artwork: {
      title: 'Блакітная нана',
      city: 'Гамбург',
      image: 'https://i.imgur.com/Sd1AgUOm.jpg',
    }
  });

  function handleNameChange(e) {
    setPerson({
      ...person,
      name: e.target.value
    });
  }

  function handleTitleChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        title: e.target.value
      }
    });
  }

  function handleCityChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        city: e.target.value
      }
    });
  }

  function handleImageChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        image: e.target.value
      }
    });
  }

  return (
    <>
      <label>
        Імя:
        <input
          value={person.name}
          onChange={handleNameChange}
        />
      </label>
      <label>
        Назва:
        <input
          value={person.artwork.title}
          onChange={handleTitleChange}
        />
      </label>
      <label>
        Горад:
        <input
          value={person.artwork.city}
          onChange={handleCityChange}
        />
      </label>
      <label>
        Відарыс:
        <input
          value={person.artwork.image}
          onChange={handleImageChange}
        />
      </label>
      <p>
        <i>{person.artwork.title}</i>
        {' '}
        {person.name}
        <br />
        (размешчана ў горадзе {person.artwork.city})
      </p>
      <img
        src={person.artwork.image}
        alt={person.artwork.title}
      />
    </>
  );
}

Калі капіраванне аб’ектаў у кодзе становіцца нудным, вы можаце выкарыстоўваць бібліятэку Immer, каб паменшыць колькасць кода, што паўтараецца:

{
  "dependencies": {
    "immer": "1.7.3",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "use-immer": "0.5.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {}
}

Ready to learn this topic?

Звярніцеся да старонкі «Абнаўленне аб’ектаў у стане» каб даведацца як правільна абнаўляць аб’екты.

Read More

Абнаўленне масіваў у стане

Масівы — гэта яшчэ адзін тып зменлівых аб’ектаў JavaScript, які можна захоўваць у стане і які мусіць разглядацца як значэнне толькі для чытання. Як і ў выпадку з аб’ектамі, калі вы хочаце абнавіць масіў, які захоўваецца ў стане, вам трэба стварыць новы (або зрабіць копію існуючага), а затым абнавіць стан так, каб ён пачаў выкарыстоўваць новы масіў:

import { useState } from 'react';

const initialList = [
  { id: 0, title: 'Вялікі пузы', seen: false },
  { id: 1, title: 'Месяцовы пейзаж', seen: false },
  { id: 2, title: 'Тэракотовая армія', seen: true },
];

export default function BucketList() {
  const [list, setList] = useState(
    initialList
  );

  function handleToggle(artworkId, nextSeen) {
    setList(list.map(artwork => {
      if (artwork.id === artworkId) {
        return { ...artwork, seen: nextSeen };
      } else {
        return artwork;
      }
    }));
  }

  return (
    <>
      <h1>Art Bucket List</h1>
      <h2>Мой спіс мастацтва для прагляду:</h2>
      <ItemList
        artworks={list}
        onToggle={handleToggle} />
    </>
  );
}

function ItemList({ artworks, onToggle }) {
  return (
    <ul>
      {artworks.map(artwork => (
        <li key={artwork.id}>
          <label>
            <input
              type="checkbox"
              checked={artwork.seen}
              onChange={e => {
                onToggle(
                  artwork.id,
                  e.target.checked
                );
              }}
            />
            {artwork.title}
          </label>
        </li>
      ))}
    </ul>
  );
}

Калі капіраванне масіваў у кодзе становіцца нудным, вы можаце выкарыстоўваць бібліятэку Immer, каб паменшыць колькасць кода, што паўтараецца:

{
  "dependencies": {
    "immer": "1.7.3",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "use-immer": "0.5.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {}
}

Ready to learn this topic?

Звярніцеся да старонкі «Абнаўленне масіваў у стане» каб даведацца як правільна абнаўляць масівы.

Read More

Што далей

Перайдзіце да старонкі «Рэагаванне на падзеі», каб пачаць чытаць гэтую главу старонка за старонкай!

Або, калі вы ўжо знаёмыя з гэтымі тэмамі, чаму б не пачытаць пра «Кіраванне станам»?