函数式组件+hook
Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。
React 内置了一些像
useState
这样的 Hook。你也可以创建你自己的 Hook 来复用不同组件之间的状态逻辑。
先看一下使用代码:实现一个基本的计数器
- 第一行: 引入 React 中的
useState
Hook。它让我们在函数组件中存储内部 state。- 第三行: 在 App组件内部,我们通过调用
useState
Hook 声明了一个新的 state 变量。它返回一对值给到我们命名的变量上。我们把变量命名为count
,因为它存储的是点击次数。我们通过传0
作为useState
唯一的参数来将其初始化为0
。第二个返回的值本身就是一个函数。它让我们可以更新count
的值,所以我们叫它setCount
。- 第七行: 当用户点击按钮后,我们传递一个新的值给
setCount
。React 会重新渲染 App 组件,并把最新的count
传给它。
import React,{useState} from 'react'
function App(){
//声明一个叫count的state变量
// 使用数组解构的语法,让我们在调用userState的时候给state变量取不同的名字
const [count,setCount]=useState(12)
return(
<>
{count}
>
)
}
export default App
首次渲染时,组件内部代码会被执行一次,其中useState也会被执行,重点注意:初始值只有在首次渲染时生效
更新渲染:setcount也会更新,
1.组件App会再次渲染,这个函数会再次执行
2.useState再次执行时,得到的新的count不是0而是新修改的1,模板会用新值渲染
注意:不能写count++或count--;写成count+1;或者count-1
它定义一个 “state 变量”。我们的变量叫
count
, 但是我们可以叫他任何名字,比如banana
。这是一种在函数调用时保存变量的方式 ——useState
是一种新方法,它与 class 里面的this.state
提供的功能完全相同。一般来说,在函数退出后变量就会”消失”,而 state 中的变量会被 React 保留
useState
需要哪些参数?
useState()
方法里面唯一的参数就是初始 state。不同于 class 的是,我们可以按照需要使用数字或字符串对其进行赋值,而不一定是对象。在示例中,只需使用数字来记录用户点击次数,所以我们传了0
作为变量的初始 state。(如果我们想要在 state 中存储两个不同的变量,只需调用useState()
两次即可。)
useState
方法的返回值是什么? 返回值为:当前 state 以及更新 state 的函数。这就是我们写
const [count, setCount] = useState()
的原因。这与 class 里面this.state.count
和this.setState
类似,唯一区别就是你需要成对的获取它们。
这种 JavaScript 语法叫数组解构。它意味着我们同时创建了 count 和
setCount
两个变量,count 的值为 useState 返回的第一个值,setCount 是返回的第二个值。它等价于下面的代码:
var fruitStateVariable = useState('banana'); // 返回一个有两个元素的数组
var fruit = fruitStateVariable[0]; // 数组里的第一个值
var setFruit = fruitStateVariable[1]; // 数组里的第二个值
当我们使用 useState
定义 state 变量时候,它返回一个有两个值的数组。第一个值是当前的 state,第二个值是更新 state 的函数。使用 [0]
和 [1]
来访问
注意:数组中名字可以改变,顺序不可以改变
Effect Hook 可以让你在函数组件中执行副作用操作
那么什么是副作用呢?
数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。
先看一段基本使用的代码
点击计数器,在title标签栏同步显示计数结果
import React,{useState,useEffect} from 'react'
function App(){
const [count,setCount]=useState(1)
useEffect(()=>{
document.title=count
})
return(
<>
{count}
>
)
}
export default App
如果你看懂了上面代码,熟悉生命周期函数的话就会发现
useEffect
Hook 看做 componentDidMount
,componentDidUpdate
和 componentWillUnmount
这三个函数的组合。
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
render() {
return (
You clicked {this.state.count} times
);
}
}
注意:这样我们就要在两个生命周期中写同样的代码,就算封装成函数,也得调用两次
这个时候你回过头看看最初使用useEffect的代码
在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。
给userEffect(),传入第二个参数,特定数据进行调用
如下代码,定义两个计数器使用第一个进行特定依赖传入,第二个不传入
import React,{useState,useEffect} from 'react'
function App(){
const [count,setCount]=useState(1)
const [count1,setCount1]=useState(1)
useEffect(()=>{
console.log('我更新了');
document.title=count
},[count])
return(
<>
{count}
{count1}
>
)
}
export default App
当我们点击第一个按钮时,可以看到调用了useEffect()
当我们点击第二个按钮时,虽然值更新了,但是没有调用useEffect
注意事项:凡是在useEffect中用到的数据,都应该在第二次参数进行说明,否则可能会有bug
总结一下:
1.不加依赖项:初始化 ,更新时都会重新渲染
2.加【】,初始化执行一次 发送网络请求用这个
3.加特定依赖项【count,name】-首次执行+数据变化时执行
不可以直接在useEffect的回调函数外层直接包裹 await ,因为异步会导致清理函数无法立即返回
useEffect(async ()=>{
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
console.log(res)
},[])
正确演示 在内部单独定义一个函数,然后把这个函数包装成同步
useEffect(()=>{
async function fetchData(){
const res = await axios.get('http://geek.itheima.net/v1_0/channels') console.log(res)
}
},[])
添加清理副作用函数后:一旦组件被销毁,定时器也被清理
import { useEffect, useState } from 'react'
function Foo() {
useEffect(() => {
const timerId = setInterval(() => {
console.log('副作用函数执行了')
}, 1000)
// 添加清理副租用函数
return () => {
clearInterval(timerId)
}
})
return Foo
}
function App() {
const [flag, setFlag] = useState(true)
return (
<>
{flag ? : null}
>
)
}
export default App
你可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。
默认情况下,它在第一次渲染之后和每次更新之后都会执行。
useEffect
?将 useEffect
放在组件内部让我们可以在 effect 中直接访问 count
state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经保存在函数作用域中。Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。
不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState
和 useEffect
调用之间保持 hook 状态的正确。