函数组件之状态,即无状态组件
使用 useState() 进行状态管理
useState()
Hook 来启用函数组件中的状态useState(initialValue)
的第一个参数initialValue
是状态的初始值[state, setState] = useState(initialValue)
返回一个包含2个元素的数组:状态值和状态更新函数- 使用新值调用状态更新器函数
setState(newState)
更新状态。或者,可以使用一个回调setState(prev => next)
来调用状态更新器,该回调将返回基于先前状态的新状态 - 调用状态更新器后,React 确保重新渲染组件,以使新状态变为当前状态
实例说明
import React, {
useState, useEffect} from 'react';
import './App.css';
function App(props) {
// 函数组件,即无状态组件,也可以使用状态
/*
* useState() 函数接受一个参数:initialState 初始状态,
* 返回一个数组,数组内有 2 个元素,可以解构
* 第 1 个元素:initialState,即 useState 函数传的值
* 第 2 个元素:是 1 个函数,可以修改 initialState 值
* 第 2 个元素函数,可以接收 3 个参数
* 第 1 个参数:更新的内容
*
* */
const [count, setCount] = useState(0);
return (
<div className="App">
<p>你点击了 {
count} 次</p>
<button onClick={
() => setCount(count + 1)}>点击</button>
</div>
);
}
export default App;
useState-initialState 初始状态
该初始值可以接受任何参数,但是记得当他接受为一个函数时,就变成了 Lazy initialization (延迟初始化) 该函数返回值即为 initialState
const [count, setCount] = useState(0);
const [count, setCount] = useState(()=>0);
// 这两种初始化方式 是相等的,但是在函数为初始值时会被执行一次
const [count, setCount] = useState(()=>{
console.log('这里只会在初始化的时候执行')
// class 中的 constructor 的操作都可以移植到这里
return 0
});
// 当第一次执行完毕后 就和另一句的代码是相同的效果了
注意:
useState-setState
也许很多人 在使用 class 的 setState 时候,会经常使用他的回调函数,
但是这里很遗憾,他只接受新的值,如果想要对应的回调,可以使用useEffect,这个问题等会会提供一个跳转链接
多种状态
- 通过多次调用useState(),一个函数组件可以拥有多个状态
function MyComponent() {
const [state1, setState1] = useState(initial1);
const [state2, setState2] = useState(initial2);
const [state3, setState3] = useState(initial3);
// ...
}
需要注意的,要确保对useState()的多次调用在渲染之间始终保持相同的顺序(后面会讲)。
我们添加一个按钮添加灯泡,并添加一个新状态来保存灯泡数量,单击该按钮时,将添加一个新灯泡。
新的状态count 包含灯泡的数量,初始值为1:
import React, {
useState } from 'react';
function Bulbs() {
const [on, setOn] = useState(false);
const [count, setCount] = useState(1);
const lightSwitch = () => setOn(on => !on);
const addBulbs = () => setCount(count => count + 1);
const bulb = <div className={
on ? 'bulb-on' : 'bulb-off'} />;
const bulbs = Array(count).fill(bulb);
return (
<>
<div className="bulbs">{
bulbs}</div>
<button onClick={
lightSwitch}>开/关</button>
<button onClick={
addBulbs}>添加灯泡</button>
</>
);
}
- [on, setOn] = useState(false) 管理开/关状态
- [count, setCount] = useState(1)管理灯泡数量。
多个状态可以在一个组件中正确工作。
状态的延迟初始化
每当 React 重新渲染组件时,都会执行useState(initialState)。 如果初始状态是原始值(数字,布尔值等),则不会有性能问题。
当初始状态需要昂贵的性能方面的操作时,可以通过为useState(computeInitialState)提供一个函数来使用状态的延迟初始化,如下所示:
function MyComponent({
bigJsonData }) {
const [value, setValue] = useState(function getInitialState() {
const object = JSON.parse(bigJsonData); // expensive operation
return object.initialValue;
});
// ...
}
getInitialState()仅在初始渲染时执行一次,以获得初始状态。在以后的组件渲染中,不会再调用getInitialState(),从而跳过昂贵的操作。
要使函数组件有状态,请在组件的函数体中调用useState()。
useState(initialState)的第一个参数是初始状态。返回的数组有两项:当前状态和状态更新函数。
const [state, setState] = useState(initialState); 使用
setState(newState)来更新状态值。
另外,如果需要根据先前的状态更新状态,可以使用回调函数setState(prevState => newState)。在单个组件中可以有多个状态:调用多次useState()。
当初始状态开销很大时,延迟初始化很方便。使用计算初始状态的回调调用useState(computeInitialState),并且此回调仅在初始渲染时执行一次。
必须确保使用useState()遵循 Hook 规则。
当闭包捕获过时的状态变量时,就会出现过时状态的问题。可以通过使用一个回调来更新状态来解决这个问题,这个回调会根据先前的状态来计算新的状态。
最后,您将使用useState()来管理一个简单的状态。为了处理更复杂的状态,一个更好的的选择是使用useReducer() hook。
useEffect
- 可以在 function 定义的组件中模拟生命周期
- 参数一:表示回调函数
- 参数二:表示什么数据改变之后会触发回调函数;
- 若没有参数二,表示所有数据改变都会触发
- 参数二为空数组,表示只有在第一次组件初始化的时候会触发
- 若参数二中的数组有值,表示数组中的数据发生改变之后会触发
实例说明
useEffect(() => {
console.log('在 dep 改变时触发,若无 dep 则,每次更新组件都会触发')
return () => {
console.log('在组件 unmount 时触发')
};
});
deps 必须是一个数组,但是如果是一个空数组时:
useEffect(() => {
console.log('效果的等于 componentDidMount')
}, [])
即使有 deps ,他在初始化时也会触发一次
类组件之状态
-
state 状态:存储数据
构造函数内设置 this.state 属性,是一个对象,可以存储数据,例如:this.state = { num: 0 }; -
setState 设置状态:修改数据
setState 是一个函数,需要调用,可以操作构造函数 this.state 对象内 的属性,并且跟新视图,且会触发 render 函数调用 setState 方法,通过 this.setState(),setState 方法接收 2 个参数
第 1 个参数:对象或者回调函数 是 1 个对象:对象内属性名 就是 构造函数 this.state 对象内的属性名
通过修改属性值,可以修改 this.state 对象内属性值,并更新视图和触发 render() 函数,例如:this.setState({
num: this.state.num + 1 }) 是回调函数:回调函数,接收 2 个参数 第 1 个参数:state
状态对象(存储的数据) 第 2 个参数:props 属性对象(父类通过属性传递的值存在 props 对象内) 回调函数内 return
一个的对象,对象属性名为 state 需要修改的属性名,例如:return { n: prevState.n + 1 }
第 2 个参数:是 1 个回调函数,数据更新完成后触发该函数,例如:this.setState({ num: this.state.num + 1 },()=>{console.log(‘数据更新完成后触发该函数’);})
注意:
更改 state 的值,不能直接改,直接更改视图不会同步,要更改 state 用 setState 更改
连续执行多次 this.setState() 函数会执行最后一个 this.setState()
this.setState() 是异步函数,先执行同步代码后执行,this.setState(),因此要得到执行完 setState 后的值,需要在第 2 个参数,回调函数内获取
组件内,事件函数没有 this ,需要通过 bind 绑定 render 内 this,并可以通过 bind 传值
setState() 第 1 个参数是 对象
- state 状态:存储数据
构造函数内设置 this.state 属性,是一个对象,可以存储数据,例如:this.state = { num: 0 }; - setState 设置状态:修改数据
setState 是一个函数,需要调用,可以操作构造函数 this.state 对象内的属性,并且跟新视图,且会触发 render 函数
调用 setState 方法,通过 this.setState(),setState 方法接收 2 个参数
第 1 个参数:对象或者回调函数 是 1 个对象:对象内属性名 就是 构造函数 this.state 对象内的属性名
通过修改属性值,可以修改 this.state 对象内属性值,并更新视图和触发 render() 函数,例如:this.setState({
num: this.state.num + 1 }) 第 2 个参数:是 1
个回调函数,数据更新完成后触发该函数,例如:this.setState({ num: this.state.num + 1
},()=>{console.log(‘数据更新完成后触发该函数’);})
实例说明
- 构造函数内设置 this.state{num:1}
- 组件挂载完成后更改 state 内的值 this.setState({ num: this.state.num + 1 })
- 渲染效果为 2
<div id="app"></div>
<script type="text/babel">
class App extends React.Component {
constructor(props) {
// 1 先初始化
super(props);
// super() 传 props 与 this.props=props; 等价的
// 父组件也是做的这个事 this.props=props;
// 执行 super() 只是继承了父组件的实例化属性和方法
// this.props=props;
// state 存储数据
this.state = {
num: 1
}
}
componentDidMount() {
// 3 渲染完成触发
// 改值后自动触发 render
this.setState({
// 连续执行多次 this.setState() 函数会执行最后一个 this.setState()
num: this.state.num + 1
}, () => {
console.log('数据更新完成后触发')
})
}
render() {
// 2 渲染
return (
<div>
{
this.state.num}{
/* 渲染 2 */}
</div>
)
}
}
ReactDOM.render(<App/>, document.querySelector('#app'));
</script>