Часть 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 .
Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.