Русский
Русский
English
Статистика
Реклама

Перевод Заметка о том, как React обновляет состояние



Доброго времени суток, друзья!

Хук useState() управляет состоянием в функциональных компонентах React. В классовых компонентах состояние хранится в this.state, а для обновления вызывается метод this.setState().

Обычно, в работе с состоянием нет ничего сложного. Тем не менее, существует один важный нюанс, связанный с его обновлением.

Каким образом состояние обновляется: незамедлительно (синхронно) или отложенно (асинхронно)? Читайте дальше, чтобы узнать ответ.

1. Обноление состояния с помощью useState()


Допустим у нас имеется такой функциональный компонент:

import { useState } from 'react'function DoubleIncreaser() {  const [count, setCount] = useState(0)  const doubleIncreaseHandler = () => {    setCount(count + 1)    setCount(count + 1)  }  return (    <>      <button onClick={doubleIncreaseHandler}>        Double Increase      </button>      <div>Count: {count}</div>    </>  )}

const [count, setCount] = useState(0) определяет начальное состояние компонента. count переменная, содержащая текущее состояние, а setCount функция обновления этого состояния.

Компонент содержит кнопку Double Increase. При нажатии на эту кнопку вызывается обработчик doubleIncreaseHandler, осуществляющий два последовательных обновления count: setCount(count + 1) и затем еще раз setCount(count + 1).

Каким будет состояние компонента после нажатия кнопки, 1 или 2?

Откройте это демо и нажмите на кнопку. Значение count будет увеличиваться на 1 после каждого клика.

Когда setCount(count + 1) обновляет состояние, значение count не изменяется сразу. Вместо этого, React планирует обновление состояния и при следующем рендеринге в выражении const [count, setCount] = useState(0) хук присваивает count новое значение.

Например: если значением переменной count является 0, то вызов setCount(count + 1); setCount(count + 1) оценивается как setCount(0 + 1); setCount(0 + 1) что приводит к 1 как значению состояния при следующем рендеринге.

Таким образом, обновление состояния с помощью setValue(newValue) в выражении [value, setValue] = useState() осуществляется асинхронно.

Однако, функция обновления состояния может принимать коллбэк в качестве аргумента для вычисления нового состояния на основе текущего. В нашем случае мы можем использовать setCount(actualCount => actualCount + 1):

import { useState } from 'react'function DoubleIncreaser() {  const [count, setCount] = useState(0)  const doubleIncreaseHandler = () => {    setCount(actualCount => actualCount + 1)    setCount(actualCount => actualCount + 1)  }  return (    <>      <button onClick={doubleIncreaseHandler}>        Double Increase      </button>      <div>Count: {count}</div>    </>  )}

При обновлении состояния с помощью такой функции аргумент actualCount будет содержать актуальное значение состояния.

Откройте это демо и нажмите на кнопку. Значение count увеличится до 2, как и ожидается.

Разумеется, мы всегда можем создать промежуточную переменную:

import { useState } from 'react'function DoubleIncreaser() {  const [count, setCount] = useState(0)  const doubleIncrease = () => {    let actualCount = count    actualCount = actualCount + 1    actualCount = actualCount + 1    setCount(actualCount)  }  return (    <>      <button onClick={this.doubleIncrease}>        Double Increase      </button>      <div>Count: {count}</div>    </>  )}

let actualCount = count это промежуточная переменная, которую можно обновлять как угодно. Эта переменная используется для обновления состояния с помощью setCount(actualCount).

2. Состояние иммутабельно (неизменяемо) и доступно только для чтения


Если вы забудете о том, что состояние обновляется при следующем рендеринге, вы можете попытаться прочитать значение сразу после его изменения. К сожалению, у вас ничего не выйдет:

function FetchUsers() {  const [users, setUsers] = useState([])  useEffect(() => {    const startFetching = async () => {      const response = await fetch('/users')      const fetchedUsers = await response.json()      setUsers(fetchedUsers)      console.log(users)        // => []      console.log(fetchedUsers) // => ['John', 'Jane', 'Alice', 'Bob']    }    startFetching()  }, [])  return (    <ul>      {users.map(user => <li>{user}</li>)}    </ul>  )}

Компонент FetchUsers отправляет запрос при монтировании startFetching().

При получении данных setUsers(fetchedUsers) обновляет состояние. Тем не менее, изменения не происходят сразу.

Переменная users иммутабельна и доступна только для чтения. Только хук useState() может присвоить ей новое значение. Напрямую этого делать нельзя:

  function FetchUsers() {    const [users, setUsers] = useState([])    useEffect(() => {      const startFetching = async () => {        const response = await fetch('/users')        const fetchedUsers = await response.json()        users = fetchedUsers       // Неправильно! users доступна только для чтения        users.push(...fetchedUsers) // Неправильно! users иммутабельна        setUsers(fetchedUsers)     // Правильно!      }      startFetching()    }, [])    return (      <ul>        {users.map(user => <li>{user}</li>)}      </ul>    )  }

3. Обновление состояния в классовом компоненте


Асинхронное обновление состояния характерно и для классовых компонентов.

Рассмотрим пример:

import { Component } from 'react';class DoubleIncreaser extends Component {  state = {    count: 0  };  render() {    return (      <>        <button onClick={this.doubleIncrease}>          Double Increase        </button>        <div>Count: {this.state.count}</div>      </>    );  }  doubleIncrease = () => {    // Работает!    this.setState(({ count }) => ({      count: count + 1    }));    this.setState(({ count }) => ({      count: count + 1    }));    // Не работает!    // this.setState({ count: this.state.count + 1 });    // this.setState({ count: this.state.count + 1 });  }}

Обратите внимание на обработчик doubleIncrease(): для обновления состояния в нем используется функция обратного вызова.

Откройте это демо и нажмите на кнопку. Значение this.state увеличится до 2.

В классовых компонентах this.state также не обновляется моментально. При вызове this.setState(newState) React откладывает обновление this.state до следующего рендеринга.

Таким образом, this.setState(newState) обновляет this.state асинхронно.

4. Заключение


Хук useState() и this.setState() (внутри классового компонента) обновляют значение переменной и состояние компонента асинхронно.

Запомните простое правило: вызов сеттера setState(newValue) хука useState() (или this.setState()) обновляет состояние не сразу, а при очередном рендеринге компонента.

Вы заметили, что React теперь достаточно импортировать только один раз (в index.js)? В компонентах этого делать больше не нужно.

Благодарю за внимание и хорошего дня.
Источник: habr.com
К списку статей
Опубликовано: 22.12.2020 14:23:50
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка веб-сайтов

Javascript

Программирование

Reactjs

React.js

React

State

Update

Usestate

Setstate

Состояние

Обновление

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru