像专业人士一样使用 useState() ✨
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
这篇博文涵盖了关于状态和 React Hook 的所有概念,useState从基础到高级模式。本文假设你已经了解 React 的基础知识,例如组件、属性和 JSX。
counter是hello world国家管理。
什么是状态?为什么 React 需要状态管理?⚛️
function Counter() {
// State: a counter value
const [counter, setCounter] = useState(0)
// Action: code that causes an update to the state when something happens
const increment = () => {
setCounter(counter + 1)
}
// View: the UI definition
return (
<div>
Value: {counter} <button onClick={increment}>Increment</button>
</div>
)
}
正如您在本例中看到的,组件主要由三个部分Counter组成。
- 国家是驱动我们应用程序的真理。
- 视图是基于状态的用户界面
- 操作是指应用程序中发生的、会改变状态的事件。
React 使用状态值(由 useState、useReducer 等 hook API 生成)来判断何时更新应用程序的 UI(视图)部分。每当状态值发生变化时,React 都会更新组件,使 UI 的状态与组件的状态保持一致。
useState Hook 🎣
useState这是一个接受一个参数作为状态初始值的函数(初始值可以是任意类型),并返回一个包含两个元素的数组:第一个元素是状态值,第二个元素是用于更新状态值的更新函数。返回的数组通常是解构的,因此我们可以随意命名变量,但最佳实践和常见约定是在set更新函数名前加上前缀。
// you can pass any data-type
setState() // if you don't pass anything than value will be updated with undefined
setState('Thanks') // String
setState(4) // Number
setState(['reading']) // array
setState({ share : 💗 }) // object
setState(null) // null
在我们的 Counter 组件中
0,是状态的初始值,我们使用解构来命名我们的两个变量counter。每次单击按钮时,都会将计数器值更新为 1setCounter。setCounter
function Counter() {
const [counter, setCounter] = useState(0)
const increment = () => {
setCounter(counter + 1)
}
return (
<div>
Value: {counter} <button onClick={increment}>Increment</button>
</div>
)
}
状态的惰性初始化🦥
每次 React 重新渲染组件时,useState(initialState)都会执行该函数。如果初始状态是一些耗时的函数计算,例如从 localStorage 读取数据、处理大量数据、实例具有多个方法(例如 `get`DraftJs或 ` ThreeJsget` 实例),那么组件可能会遇到一些性能问题。
// format : useState(() => initalState)
const [token, setToken] = useState(() => window.localStorage.getItem('token') || '')
我们可以使用延迟初始化来避免性能瓶颈,你只需要将初始状态放入函数中即可。
使用回调更新状态🤙
const [counter, setCounter] = useState(0);
const increment = () => {
setCounter(counter + 1);
setTimeout(() => {
setCounter(counter + 1);
}, 1000);
};
我们已经修改了前面示例中的递增函数,现在我们在函数中添加了异步行为,你认为输出结果会是什么?
请稍作停顿思考,
前方剧透!
你会看到,点击按钮一次后,即使我们有 2 次setCounter通话,我们仍然会得到一个更新后的新计数,其中只有 1。
所以到底发生了什么?🤔
问题在于第二次调用得到的setCounter计数器值与第一次调用得到的计数器值相同。在这个例子中,两次调用都setCounter得到了相同的计数器值,0因此它们都将其更新为1。
但是为什么更新程序第二次获取到的值为0呢?😕
为此,你需要了解 React 中重新渲染的实际工作原理。我们不会深入探讨,但简而言之,重新渲染意味着如果你的状态发生变化,整个组件都会被新的组件替换。在这个例子中,`whole`Counter会被再次调用,然后获取新的值。这里我们使用了多次连续更新,并且由于闭包的 setCounter存在,我们可以访问counter从数组解构中得到的变量 `one`,其值为 0。
0在这个例子中,当按钮被点击时,初始值从 0 更新为 1,但为了获得更新后的状态 (1),React 需要重新渲染组件,但在这里我们再次调用它来将计数器更新为 +1,并且在一秒钟后更新值时,setCounter计数器会像这样得到。01
对于大多数
async活动来说useEffect,这将是更好的选择。对于更复杂的状态也是如此useReducer。
解决方案 🔥
当新状态依赖于先前的状态时,您可以使用回调函数更新状态。
const increment = () => {
setCounter(counter + 1);
setTimeout(() => {
// callback inside a updater function
setCounter(counter => counter + 1);
}, 1000);
};
如果你用这个新的函数替换原来的增量函数,你将得到的是对内部状态的引用,而不是状态的闭包值。
使用案例💼
// toggle a boolean
const [toggled, setToggled] = useState(false);
setToggled(toggled => !toggled);
// Update an object
const [size, setSize] = useState({ height : 500, width : 800})
setSize(currentSize => ({...currentSize , height : 700}))
// Update items in array
const [items, setItems] = useState([]);
setItems(items => [...items, 'push']);