useState挂钩使我们可以使功能组件成为有状态的。
创建和初始化状态
调用时,useState
返回两个项目的数组。第一个是我们的状态值,第二个是用于设置或更新该值的函数。所述useState
钩接受一个参数,对所述相关联的状态的,它可以是任何的Javascript数据类型的初始值。
我们使用数组解构将这两个返回值分配给变量。
import React, { useState } from 'react';
const Component = () => {
const [value, setValue] = useState(initial value)
...
由于数组元素没有名称,因此我们可以根据需要命名这两个变量。声明更新程序函数名称的一般约定是从set开头,以状态变量的名称结尾,所以[value, setValue]
。传入的初始状态参数将是在第一个渲染器上分配给状态变量的值。
几种具有各种数据类型的状态的例子
每个状态都有其自己的调用useState
以及它自己的变量和用于设置/更新它的函数。
const [count, setCount] = useState(0)
const [color, setColor] = useState('#526b2d')
const [isHidden, setIsHidden] = useState(true)
const [products, setProducts] = useState([])
const [user, setUser] = useState({
username: '',
avatar: '',
email: '',
})
Count是我们计划递增或递减的数字,初始值为0。Color的初始值为包含哈希码的字符串,默认值为绿色。isHidden是一个布尔值,其初始值为true,我们可以假设它描述了DOM中某些东西的可见性,这些东西将在隐藏和可见之间切换。产品的初始值是一个空数组,我们计划在其中填充最有可能从API提取的产品列表。用户是具有多个属性的对象,所有属性均默认为空字符串。
初始化昂贵状态
如果您的值计算起来很昂贵,例如必须过滤和处理一系列项目,则可以将初始化包装在函数中,这样,useState
它将仅调用一次函数,而不是在每次渲染时都调用该函数。
const [filteredList, setFilteredList] = useState(() => listOf10MillionItems.filter())
更新基本类型
使用状态更新状态变量将useState
始终替换先前的状态。这意味着更新原始类型(字符串,布尔值,数字)很简单,因为它们的值被替换而不是被突变。
这是经典且简单的计数器组件示例。我们要增加或减少存储在状态中的数字并向用户显示该数字或将该数字重置为0。
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)
const decrement = () => setCount(count - 1)
const reset = () => setCount(0)
return (
<div className='counter'>
<p className='count'>{count}</p>
<div className='controls'>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
</div>
)
}
export default Counter
更新数组和对象
当使用来更新处于状态的数组或对象时useState
,您必须记住在替换状态时将整个对象或数组传递给updater函数,而不是像setState
在基于类的组件中找到的方法那样进行合并。
数组
const [items, setItems] = useState([])
// Completely replaces whatever was stored in the items array
setItems([{item1}, {item2}])
// Don't use JS array methods such as pop, push, shift, unshift
// as these will not tell React to trigger a re-render.
items.push({item3})
// Instead, make a copy of the array then add your new item onto the end
setItems([...items, {item3}])
// To update an item in the array use .map.
// Assumes each array item is an object with an id.
setItems(
items.map((item, index) => {
item.id === id ? newItem : item
})
)
对象
const Person = () => {
const [person, setPerson] = useState({
firstName: '',
lastName: ''
});
const handleChange = (e) => {
setPerson({
...person,
[e.target.name]: e.target.value
});
};
const handleSubmit = (e) => {
e.preventDefault()
// Form submission logic here.
}
return (
<form>
<label htmlFor='first'>
First Name:
<input
id='first'
name='firstName'
type='text'
value={person.firstName}
onChange={handleChange}
/>
</label>
<label htmlFor='last'>
Last Name:
<input
id='last'
name='lastName'
type='text'
value={person.lastName}
onChange={handleChange}
/>
</label>
<button type='submit' onClick={handleSubmit}>Submit</button>
</form>
);
};
在上面的例子中,handleChange
函数调用setPerson
使用并传递从状态人对象的传播算子与...person
。如果不传入状态存储的现有人员对象,则只要输入值之一发生更改,整个对象都将被覆盖。
嵌套对象和数组
要更新嵌套的对象和数组,需要像上述示例一样不变地复制和更新每个级别。
const [people, setPeople] = useState({
jerry: {
firstName: 'Jerry',
lastName: 'Garcia',
address: {
street: '710 Ashbury Street',
city: 'San Francisco',
state: 'CA',
zip: '94117'
}
},
jim: {
firstName: 'Jim',
lastName: 'Morrison',
address: {
street: '8021 Rothdell Trail',
city: 'Los Angeles',
state: 'CA',
zip: '90046'
}
}
})
// Jerry is gonna move next door
setPeople({
// Copy people
...people,
// Overwrite person you want to update
jerry: {
// Copy Jerry's existing properties
...people.jerry,
// Overwrite Jerry's address
address: {
// Copy everything over from Jerry's original address
...people.jerry.address,
// Update the street
street: '712 Ashbury Street'
}
}
})
复杂状态
如果您具有多个值的复杂状态,则将它们存储起来useState
会很麻烦。另一个称为Hook的钩子 useReducer
更适合于管理具有多个值的状态。