前言:最近公司进行了技术分享,刚好我的选题是react-hooks,近期就给大家简单介绍下react-hooks的常用函数以及需要注意的事项(尤其是react-hooks里的capture value 特性,写业务经常遇到的坑!!!) 欢迎大家相互交流指正。话不多说,开搞开搞!!
一、Hook简介以及引入的目的
React Hooks就是用函数的形式代替原来的继承类的形式,并且使用预函数的形式管理state,有Hooks可以不再使用类的形式定义组件了。也就是说原来把组件分为有状态组件和无状态组件,有状态组件用类的形式声明,无状态组件用函数的形式声明。那现在所有的组件都可以用函数来声明了。
使用 React Hooks 相比于从前的类组件有以下几点好处:
1 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者不利于维护和迭代,通过 React Hooks 可以将功能代码聚合,方便阅读维护
2 组件树层级变浅,在原本的代码中,我们经常使用 HOC/render props 等方式来复用组件的状态,增强功能等,无疑增加了组件树层数及渲染,而在 React Hooks 中,这些功能都可以通过强大的自定义Hooks 来实现,就是可以利用自定义Hooks来进行组件树层的渲染。
注意:
- 只能在 函数内部的最外层 调用 Hook,不要在循环、条件判断或者子函数中调用
- 只能在 React 的函数组件中调用 Hook,不要在其他 JavaScript 函数中调用
二、React Hooks 编写形式对比
需求:点我们点击按钮时,点击数量不断增加。
Class组件的写法:在类组件中,我们使用 this.state 来保存组件状态,并对其修改触发组件重新渲染。
import React, { Component } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.state = { count:0 }
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.addCount.bind(this)}>Chlick me</button>
</div>
);
}
addCount(){
this.setState({count:this.state.count+1})
}
}
export default Example;
React Hooks 写法:
import React, { useState } from 'react';
function Example(){
const [ count , setCount ] = useState(0); // 这里就是用hook函数useState进行数据修改
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()=>{setCount(count+1)}}>click me</button>
</div>
)
}
export default Example;
从这两个程序的对比上可以看出Hooks本质上就是一类特殊的函数,注意!!! React Hooks不能在if...else...这样的条件语句中进行调用,必须要按照相同的顺序进行渲染。总之:就是React Hooks不能出现在条件判断语句中,因为它必须有完全一样的渲染顺序。
三、useState 保存组件状态
useState是react自带的一个hook函数,它的作用是用来声明状态变量。
那我们从三个方面来看useState的用法,分别是声明、读取、更新(修改)。这三个方面掌握了,你基本也就会使用useState了。
3.1.1、 声明:
代码如下:
这种方法是ES6语法中的数组解构
const [ count , setCount ] = useState(0); //这是初始值0传入
如果不写成数组解构,上边的语法要写成下面的三行:
let _useState = userState(0)
let count = _useState[0]
let setCount = _useState[1]
# useState这个函数接收的参数是状态的初始值(Initial state),它返回一个[数组],这个数组的第0位是当前的状态值,第1位是可以改变状态值的方法函数。
useState接收的参数是状态的初始值,返回的是一个数组, 数组第0位是当前状态值,第1位是可以改变状态值的方法函数 .所以上面的代码的意思就是声明了一个状态变量为count,并把它的初始值设为0,同时提供了一个可以改变count的状态值的方法函数。
3.1.2、读取
<p>You clicked {count} times</p>
我们读取是很简单的。只要使用{count}就可以,因为这时候的count就是JS里的一个变量,想在JSX中使用,值用加上{}就可以。
3.1.3、更新
更新分为以下两种方式,即直接更新和函数式更新,其应用场景的区分点在于:
直接更新不依赖于旧 state 的值;函数式更新依赖于旧 state 的值;
引入问题:为什么函数组件每次渲染都会重新进行调用把函数体重新走一次, 但是useState的值却没有一直被赋予初值,
// 直接更新
setState(newCount); / 不依赖旧的 state 值
// 函数式更新
setState(prevCount => prevCount + 1); / 依赖旧的 state 值
<button onClick={()=>{setCount(count+1)}}>click me</button> / 直接更新
<button onClick={()=>{setCount((count)=>count+1)}}>click me</button> / 函数式更新
直接调用setCount函数,这个函数接收的参数是修改过的新状态值。接下来的事情就交给React, 如果状态值发生改变,他会重新渲染组件。并且注意,state值不可以直接修改,只能通过setState来进行修改!!
案例:对直接更新和函数式更新区别的例子:
3.2、实现合并
注意: useState 是 直接替换 它的要 被更新对象 ; 我们可以用函数式的 setState 结合展开运算符( ... )来达到合并更新对象的效果。
const [value,setValue]=useState({
name:'',
age:''
})
=====================
setState(value => {
// 也可以使用 Object.assign
return {...value, ...updatedValues};
});
如果要修改原数据其中某一个属性值
setState({...value, name:'张三'})
3.3、惰性初始化 state,处理复杂的逻辑
initialState 初始参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。其应用场景在于:创建初始 state 很昂贵时,例如需要通过复杂计算获得;那么则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用,注意一点,这个函数只能进行同步操作!!!!
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
=========实际案例===========
const sum = (a, b) => {
return a + b
}
const [initData, setInitData] = useState(() => {
const a = sum(1, 2)
return a;
})
return (
<>{initData} </>
)
重要: 对比箭头函数写法与直接调用函数:
3.4、小综合案例:
case: useState 初始值传入的是一个对象,然后对这个对象进行修改值
import React, { useState } from "react";
function App() {
const [obj, setObject] = useState({
count: 0,
name: "alife"
});
return (
<div className="App">
Count: {obj.count}
<button onClick={() => setObject({ ...obj, count: obj.count + 1 })}>+</button>
<button onClick={() => setObject({ ...obj, count: obj.count - 1 })}>-</button>
</div>
);
}
3.5、一些重点
-
使用多个 state 变量,用相应的set函数进行修改state状态;
-
注意,State Hook 只有更新了state 值才会引起组件渲染
-
如果使用函数改变数据, 若数据和之前的数据完全相等(使用Object.属性名 eg-> obj:{name:'张三'} obj.name), 则不会重新渲染, 但如果是引用类型的,仍会进行赋值
-
setState在原生事件和setTimeout、setInterval里是同步进行的(原因在于这些事件帮助state脱离了React的掌控)
讨论题: 为什么useState不能写在if判断里?