Добавление интерактивности
Некоторые элементы на экране обновляются в ответ на действия пользователя. Например, щелчок по галерее изображений меняет активное изображение. В React данные, которые изменяются со временем, называются состоянием. Вы можете добавить состояние в любой компонент и обновлять его по мере необходимости. В этой главе вы узнаете, как создавать компоненты, обрабатывающие взаимодействия, обновляющие их состояние и отображающие различные результаты с течением времени.
В этой главе
- Как обрабатывать события, инициированные пользователем
- Как сделать компоненты «помнящими» информацию с помощью состояния
- Как React обновляет пользовательский интерфейс в двух фазах
- Почему состояние не обновляется сразу после изменения
- Как поставить в очередь несколько обновлений состояния
- Как обновить объект в состоянии
- Как обновить массив в состоянии
Обработка событий
React позволяет добавлять обработчики событий в ваш JSX. Обработчики событий — это ваши собственные функции, которые будут вызываться в ответ на взаимодействия пользователя, такие как щелчки, наведение курсора, фокусировка на элементах формы и так далее.
Встроенные компоненты, такие как <button>, поддерживают только встроенные события браузера, такие как onClick. Однако вы также можете создавать собственные компоненты и давать их обработчикам событий любые специфичные для приложения имена, которые вам нравятся.
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Playing!')} onUploadImage={() => alert('Uploading!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Play Movie </Button> <Button onClick={onUploadImage}> Upload Image </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
Хотите узнать об этом больше?
Прочитайте Обработка событий, чтобы узнать, как добавить обработчики событий.
Читать далееСостояние: память компонента
Компонентам часто необходимо изменять содержимое экрана в результате взаимодействия. Ввод текста в форму должен обновлять поле ввода, нажатие «Далее» на карточке изображений должно менять отображаемое изображение, нажатие «Купить» добавляет продукт в корзину. Компонентам нужно «помнить» вещи: текущее значение ввода, текущее изображение, корзину покупок. В 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}> Next </button> <h2> <i>{sculpture.name} </i> by {sculpture.artist} </h2> <h3> ({index + 1} of {sculptureList.length}) </h3> <button onClick={handleMoreClick}> {showMore ? 'Hide' : 'Show'} details </button> {showMore && <p>{sculpture.description}</p>} <img src={sculpture.url} alt={sculpture.alt} /> </> ); }
Хотите узнать об этом больше?
Прочитайте Состояние: память компонента, чтобы узнать, как запомнить значение и обновить его при взаимодействии.
Читать далееРендеринг и подтверждение
Перед отображением компонентов на экране React должен их отрисовать. Понимание этапов этого процесса поможет вам думать о том, как выполняется ваш код, и объяснять его поведение.
Представьте себе, что ваши компоненты — это повара на кухне, собирающие вкусные блюда из ингредиентов. В этом случае React — это официант, который принимает заказы от клиентов и приносит им их блюда. Этот процесс запроса и подачи пользовательского интерфейса состоит из трех этапов:
- Инициирование отрисовки (передача заказа от клиента на кухню)
- Отрисовка компонента (приготовление заказа на кухне)
- Подтверждение в DOM (выдача заказа клиенту)
Инициирование Отрисовка Подтверждение
Иллюстрация предоставлена Рэйчел Ли Наборс
Хотите узнать об этом больше?
Прочитайте Рендеринг и подтверждение, чтобы узнать о жизненном цикле обновления пользовательского интерфейса.
Читать далееСостояние как моментальная фотография
В отличие от обычных переменных JavaScript, состояние React ведет себя больше как моментальная фотография. Установка значения не изменяет уже имеющуюся переменную состояния, а вместо этого запускает повторную отрисовку. Это может показаться неожиданным на первый взгляд!
console.log(count); // 0
setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 0! Это поведение помогает избежать скрытых ошибок. Вот небольшой чат-приложение. Попробуйте угадать, что произойдет, если вы нажмете «Отправить», а затем измените получателя на Боба. Чье имя отобразится в alert через пять секунд?
import { useState } from 'react'; export default function Form() { const [to, setTo] = useState('Alice'); const [message, setMessage] = useState('Hello'); function handleSubmit(e) { e.preventDefault(); setTimeout(() => { alert(`You said ${message} to ${to}`); }, 5000); } return ( <form onSubmit={handleSubmit}> <label> To:{' '} <select value={to} onChange={e => setTo(e.target.value)}> <option value="Alice">Alice</option> <option value="Bob">Bob</option> </select> </label> <textarea placeholder="Message" value={message} onChange={e => setMessage(e.target.value)} /> <button type="submit">Send</button> </form> ); }
Хотите узнать об этом больше?
Прочитайте Состояние как моментальная фотография, чтобы узнать, почему состояние кажется «зафиксированным» и неизменным внутри обработчиков событий.
Читать далееПостановка в очередь последовательности обновлений состояния
Этот компонент содержит ошибку: нажатие «+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: {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: {score}</h1> </> ) }
Хотите узнать об этом больше?
Прочитайте Постановка в очередь последовательности обновлений состояния, чтобы узнать, как поставить в очередь последовательность обновлений состояния.
Читать далееОбновление объектов в состоянии
Состояние может содержать любые типы значений JavaScript, включая объекты. Но не следует непосредственно изменять объекты и массивы, хранящиеся в состоянии React. Вместо этого, когда вам нужно обновить объект или массив, необходимо создать новый (или скопировать существующий), а затем обновить состояние, используя эту копию.
Обычно вы будете использовать синтаксис ... для копирования объектов и массивов, которые вы хотите изменить. Например, обновление вложенного объекта может выглядеть так:
import { useState } from 'react'; export default function Form() { const [person, setPerson] = useState({ name: 'Niki de Saint Phalle', artwork: { title: 'Blue Nana', city: 'Hamburg', 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> Name: <input value={person.name} onChange={handleNameChange} /> </label> <label> Title: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> City: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Image: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {' by '} {person.name} <br /> (located in {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": {} }
Хотите узнать об этом больше?
Прочитайте Обновление объектов в состоянии, чтобы узнать, как правильно обновлять объекты.
Читать далееОбновление массивов в состоянии
Массивы — это еще один тип изменяемых объектов JavaScript, которые вы можете хранить в состоянии, и к которым следует относиться как к неизменяемым. Так же, как и с объектами, при обновлении массива, хранящегося в состоянии, необходимо создать новый (или скопировать существующий), а затем установить состояние, используя новый массив:
import { useState } from 'react'; const initialList = [ { id: 0, title: 'Big Bellies', seen: false }, { id: 1, title: 'Lunar Landscape', seen: false }, { id: 2, title: 'Terracotta Army', 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>My list of art to see:</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": {} }
Хотите узнать об этом больше?
Прочитайте Обновление массивов в состоянии, чтобы узнать, как правильно обновлять массивы.
Читать далееЧто дальше?
Перейдите по ссылке Обработка событий, чтобы начать чтение этой главы по страницам!
Или, если вы уже знакомы с этими темами, почему бы не прочитать о Управлении состоянием?
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://react.dev/learn/adding-interactivity