Часть 1: погружение в useState()

Я начинаю серию статей, чтобы поделиться своим скромным опытом использования хуков React.

Площадка: https://stackblitz.com/edit/react-ouuecu?file=src/App.js

Сначала я начну с useState, где я расскажу о трех моментах:

  • useState является асинхронным
  • установка состояния с обратными вызовами
  • Объект против атомарного состояния

useState является асинхронным

Сначала я написал статью, прежде чем рассказать о своих выводах относительно того, как ведет себя useState и как он не ведет себя мгновенным/синхронным образом. Вместо этого, когда вы устанавливаетеState с новым значением, хук запускает повторный рендеринг компонента, и состояние будет обновлено при новом рендеринге.

Давайте напишем псевдокод для вышеизложенного.

Попробуйте запустить приведенный ниже код, и вы заметите, что счетчик по-прежнему равен 0, а через секунду будет зарегистрирован 1.

const [count, setCount] = useState(0);
setCount(1);
console.log(count); // 0

Причина (наступление - это мое предположение, а не реальная причина): движок поставил в очередь перезапуск компонента*, затем продолжил обработку остального кода компонента, а затем, закончив все операции (функции ) в трассировке стека цикл обработки событий передал операцию повторного рендеринга компонента в трассировку стека и выполнил ее, тем самым обновив компонент и состояние.

* (асинхронные операции ставятся в очередь до тех пор, пока не будут выполнены функции синхронизации, поэтому они не блокируются… Подробнее об этом будет рассказано в статье useEffect)

Обновите useState с обратными вызовами

Я черпал вдохновение из этой статьи, не стесняйтесь читать ее.

Посмотрите на приведенный ниже код, попробуйте запустить его и дайте мне знать, что получилось.

  const [count, setCount] = React.useState(0);

  const increment = () => {
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
    console.log(count);
  };
  return (
    <div>
      <button onClick={increment}>increment</button>
    </div>
  )

Вывод должен выглядеть так после 3 кликов:

// 0
----
// 1
----
// 2

Теперь попробуйте запустить код ниже:

  const [count, setCount] = React.useState(0);
  const increment = () => {
    setCount(current => current + 1);
    setCount(current => current + 1);
    setCount(current => current + 1);
    console.log(count);
  };
  return (
    <div>
      <button onClick={increment}>increment</button>
    </div>
  )

Вывод должен выглядеть так после 3 кликов:

// 0
----
// 3
----
// 9

Дразнит твой мозг, да?

В первом примере кода, где мы использовали setCount(count + 1) , мы использовали состояние счетчика, которое было инициализировано как 0, когда мы выполняем функцию приращения, что произошло, так это то, что мы увеличиваем состояние счетчика 3 раза, но дело в том, что счетчик не был обновлен еще ... Итак, подумайте об этом так:

setCount(count + 1) // count is 0, so the result is 1
setCount(count + 1) // count is still 0, so the result is 1
setCount(count + 1) // count is also still 0, so the result is 1

Однако, когда мы обновили состояние с помощью обратных вызовов, как показано ниже:

setCount(function(currentState){
  return currentState + 1
})
// or
setCount(currentState => currentState + 1)

Что происходит, когда мы предоставляем функцию обратного вызова для setState, мы получаем аргумент текущего состояния… Хотя состояние счетчика не обновляется в области компонента, состояние счетчика обновляется в области setState.

Это крутой трюк, потому что теперь вам не нужно состояние для setState, вот пример useState, у которого есть только setState:

const [, setCount] = useState(0);

const increment = () => {
  setCount(curr => curr + 1)
}

Объект против атомарного состояния

В то время как `useState` и хуки в целом были построены на идее функционального программирования и предпосылке сохранения небольших и «атомарных» вещей, как показано ниже:

const [username, setUsername] = useState("")
const [email, setEmail] = useState("")
const [firstName, setFirstName] = useState("")
// ...

Хотя с этим легче читать и работать, но если вам нужно передать состояние дочерним компонентам, вы получите «ад реквизита», как показано ниже:

Вот почему я предпочитаю использовать состояние объекта, которое решает описанную выше проблему и является более кратким и читабельным.

const Parent = () => {
  const [user, setUser] = useState({
  username: "",
  email: "",
  firstName:"",
  lastName:""
  })
  
  return ( <UserInfoComponent user={user} setUser={setUser} />);
}

// UserInfoComponent.jsx

const UserInfoComponent = ({user, setUser}) => {
  return (
    <input
    type="text"
    value={user.firstName}
    onChange ={e => setUser(prev => ({...prev, firstName: e.target.value})}
    // or onChange={e => setUser({ ...user, firstName: e.target.value})}
    />
  );
}

Я использую этот подход только в том случае, если все значения моего объекта являются примитивными, а не вложенными объектами или массивами для простоты.

Спасибо за чтение.

Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .

Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.