react 中的 Hooks
今天稍微说一下 react 中的 Hooks。
React Hook/Hooks 是什么
- Hook 是 React 16.8.0 版本之后新增加的特性、语法。
- 可以让你在函数组件中使用 state 以及他的 React 特性。
State Hook
我们之前编写过一个案例,就是 点击按钮,页面数据实现 + 1 操作,当时我们写的时候都是使用 类式组件编写的,因为我们需要操作他的 state,因为类式组件有自己的 this,所以说呢,才可以操作 state,但是 函数式组件,连自己的 this 都没有,所以根本没法使用 state,但是嘿嘿,使用 Hook,便可以实现 state 的操作。
还是这个案例,我们使用 函数式组件稍微写一下,因为不能使用 state 我们就只写静态页面。
import React from 'react';
const Index = () => {
const add = () => {
console.log("按钮被点击了")
}
return (
<div>
<h1>当前数据为:0</h1>
<button onClick={add}>点击 +1</button>
</div>
);
}
export default Index;
然后我们保存看一下效果哈:
OK,点击按钮打印数据,到现在我们实现的一切ok,那具体怎么实现 函数式组件 操作 state 呢?
很简单,需要使用 useState。
import React from 'react';
const Index = () => {
// 使用 useState
const [count, setCount] = React.useState(0)
const add = () => {
setCount(count + 1) // 实现 count + 1 操作
console.log("按钮被点击了")
}
return (
<div>
<h1>当前数据为:{count}</h1>
<button onClick={add}>点击 +1</button>
</div>
);
}
export default Index;
就这么简单,看一下效果:
稍微解释一下,主要就是加了2行代码:
const [count, setCount] = React.useState(0)
上面这行代码呢,就是使用 useState() ,让你在函数组件中使用 state 以及他的 React 特性,让函数组件也可以有 state 状态, 并进行状态数据的读写操作。
React.useState(0)
他的第一个参数的意思是初始化我要维护的数据的初始值是0,他会返回一个两个元素的数组,第一个元素时内部当前数据的值,第二个元素是一个方法,即操作这个内部元素的方法。
所以说这句话的意思是:
const [count, setCount] = React.useState(0)
使用 useState 初始化内部数据为 0 并且赋值给 count,然后操作这个内部数据也就是 count 的方法,赋值给 setCount。 然后在我们的函数式组件中就可以使用 count 和 setCount 了。
所以说嘞,我们点击按钮,想让数据 + 1 的话,直接调用 setCount 设置 count 的值就可以了!
setCount(count + 1) // 实现 count + 1 操作
OK,就是这个样子。
思考
接下来呢,我们思考一个问题,就是我们点击按钮的时候,这个 Index 执行了几次?
答:N + 1 次。1 是指页面刚刚初始化完成执行的一次,N 是指我们点击按钮操作 count 的时候执行的次数。
问题来了!我们点击按钮,在之前的 count 数据基础上实现了 + 1 操作,点击操作的时候,Index 又会重新执行,那我们每次重新执行 Index 的时候,const [count, setCount] = React.useState(0) 这个初始化也会被执行啊,那为什么数据还会继续 + 1 ,而不是重新初始化到 0 呢?
单例模式!const [count, setCount] = React.useState(0) 代码在执行的时候,React 底层已经帮我们做了处理,已经把 count 维护起来了,然后我们以后再操作 Index 函数的时候,他不会在进行初始化操作。
然后还有一个问题哈,就是
setCount(count + 1) // 实现 count + 1 操作
我们还有另一种操作 setCount 的方式,和 setState 类似,传递一个函数:
const add = () => {
setCount(count => count + 1)
}
这样子的效果也是可以的,完全没有问题啊!
OK,那如果我一个页面还有别的东西呢?不光有 count ,还有个 name 需要展示呢?也简单哈,在来一个。
比如说页面除了数字,还有一个名字需要展示,点击按钮,改变名字:
import React from 'react';
const Index = () => {
const [count, setCount] = React.useState(0)
const [name, setName] = React.useState('我是ed.')
const add = () => {
setCount(count => count + 1)
}
const changeName = () => {
setName('ed.')
}
return (
<div>
<h1>当前数据为:{count}</h1>
<h1>我的名字是:{name}</h1>
<button onClick={add}>点击 +1</button>
<button onClick={changeName}>点击改变名字</button>
</div>
);
}
export default Index;
保存查看效果:
OK,关于 State Hook 相关的东西就这些了,收工!
【总结】
- State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
- 语法: const [xxx, setXxx] = React.useState(initValue)
- useState()说明:
- 参数: 第一次初始化指定的值在内部作缓存
- 返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
- setXxx()2种写法:
- setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
- setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
【本部分相关代码资料】:我是𝒆𝒅. 的 gitee
Effect Hook
OK,继续上面案例说。
现在我有一个新的需求,在上面的案例实现一个功能,我想已进入页面,也就是页面一刷新,就实现我们的 count 自加 1,每秒钟自动加 1 。
在之前的 类是组件 中我们学习生命周期的时候实现过类似的功能,在组件初始化完成的时候,开启定时器实现数据 + 1 操作的呀!!!
但是呢,我们现在使用的是 函数式组件,因为是函数式组件,所以这货没有生命周期,咋办呢?嘿嘿!React 给我们提供了 Effect Hook。
Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)。
Effect Hook 怎么使用呢?稍微复杂一点,我们一点一点看。
首先这个 useEffect 需要传入第一个参数,第一个参数是一个函数。
在上面的案例上面我们继续编写代码:
import React from 'react';
const Index = () => {
const [count, setCount] = React.useState(0)
const [name, setName] = React.useState('我是ed.')
// 使用 useEffect
React.useEffect(() => {
console.log('useEffect')
})
const add = () => {
setCount(count => count + 1)
}
const changeName = () => {
setName('ed.')
}
return (
<div>
<h1>当前数据为:{count}</h1>
<h1>我的名字是:{name}</h1>
<button onClick={add}>点击 +1</button>
<button onClick={changeName}>点击改变名字</button>
</div>
);
}
export default Index;
上面的代码很简单,就是在之前的案例加上了
// 使用 useEffect
React.useEffect(() => {
console.log('useEffect')
})
在 useEffect 我们打印了一句话,那这个代码什么时候执行呢?我们看一下效果:
我们可以看见哈,这个代码啊在我们一刷新页面的时候,数据打印了,然后我们点击 + 1 操作或者是改变名字按钮的时候,都会再次执行。
这类似生命周期钩子的哪个啊?是不是 组件挂载完成,和页面更新完成的生命周期钩子啊!但是我们只想在组件挂载完成的时候调用定时器,所以说不想让数据更新时候也执行怎么办呢?
其实 useEffect 还有第二个参数,第二个参数是一个数组,就是当哪个数据变化的时候触发,我们上面没写第二个参数,就是当这个页面只要有更新就触发,比如我们修改一下:
React.useEffect(() => {
console.log('useEffect')
}, [count])
上面代码我们传递了第二个参数,是一个数组,里面是 count,啥意思?就是除了第一次页面加载,只有在 count 数据变化的时候才触发。看一下效果,理论上,我们点击 + 1 操作的时候,会打印数据,但是修改名字的时候,应该就不变了吧?
似的,所以说呢?嘿嘿!如果我们只想在页面加载的时候触发一次,那第二个参数传递一个空数组就可以了吧?如果不传第二个参数就是监听全部变化,我传个空数组不就是除了第一次加载页面执行,其他数据变化都不监听吗。
OK,就是这样,我们根据这个思路修改代码:
React.useEffect(() => {
let timer = setInterval(() => {
setCount(count => count + 1)
}, 1000)
}, [])
这样就可以啦把!看效果!
OK,这个就实现了。
继续哈,useEffect 还有第一个参数其实是由返回值的,上面我们没有用,我们说一下简单,第一个参数是一个函数,它可以有返回值,返回值也是一个函数,在组件卸载之前调用,类似于组件将要卸载生命周期。
React.useEffect(() => {
let timer = setInterval(() => {
setCount(count => count + 1)
}, 1000)
return () => {
clearInterval(timer)
}
}, [])
好了,关于 useEffect 就是这些了。
总结一下
-
Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
-
React中的副作用操作:
- 发ajax请求数据获取
- 设置订阅 / 启动定时器
- 手动更改真实DOM
-
语法和说明:
useEffect(() => {
// 在此可以执行任何带副作用操作
return () => { // 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
- 可以把 useEffect Hook 看做如下三个函数的组合
- componentDidMount()
- componentDidUpdate()
- componentWillUnmount()
好了,本部分就到这里了。
【本部分相关代码资料】:我是𝒆𝒅. 的 gitee
Ref Hook
这个就很简单了哈,之前写的 类是组件 可以获取输入框的数据,但是函数式组件不行,但是有一个 useRef() 帮助我们实现这个功能。
比如说页面上有一个输入框,我们点击按钮获取数据库的内容,很简单的,不解释,就这一个用法,一看就懂。
import React from 'react';
import * as ReactDOM from 'react-dom';
const Index = () => {
const [count, setCount] = React.useState(0)
const [name, setName] = React.useState('我是ed.')
React.useEffect(() => {
let timer = setInterval(() => {
setCount(count => count + 1)
}, 1000)
return () => {
clearInterval(timer)
}
}, [])
// 使用 useRef
const myRef = React.useRef()
const add = () => {
setCount(count => count + 1)
}
const changeName = () => {
setName('ed.')
}
const show = () => {
alert(myRef.current.value)
}
// 卸载组件
const unmount = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
return (
<div>
<h1>当前数据为:{count}</h1>
<h1>我的名字是:{name}</h1>
<input placeholder='请输入内容' ref={myRef} />
<button onClick={add}>点击 +1</button>
<button onClick={changeName}>点击改变名字</button>
<button onClick={unmount}>卸载组件</button>
<button onClick={show}>显示输入框内容</button>
</div>
);
}
export default Index;
看效果:
好,完成
总结一下
- Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
- 语法: const refContainer = useRef()
- 作用:保存标签对象,功能与React.createRef()一样
【本部分相关代码资料】:我是𝒆𝒅. 的 gitee