没什么特殊情况的话,一般还是用函数组件
大部分内容其实是和类组件作出的对比
demo
1 | import React, { useState } from 'react'; |
好了,一个函数式组件的基本形完成了,比类组件简短很多吧
基本概念
props
与类组件基本相同,react 会向组件提供 props 参数
只不过类组件是使用构造器来接收并初始化,函数组件是直接通过传入参数来取得
state
对于私有变量,类组件的方案是在构造器中声明
而在函数组件中,要使用 useState
来模拟 state 的效果
useState
函数返回两个值,按顺序分别是目标变量的值和对于该变量的 setter
同时还接受一个传入参数,作为目标变量的初始值
可以通过解构赋值获得变量和 setter
1 | const [n, setN] = useState(0) |
setter
对于从 useState
中取得的 setter,一般可以直接写一个值,例如
1 | setN(n + 1) |
但是有复杂逻辑时,不免太过单薄,所以一般建议在 setter 中传入一个 function
setter 会向这个函数抛出一个参数,这个参数是操作数当前的值
这个函数应当返回一个值,作为操作数的新值
返回值
函数组件要求 return 一段 JSX 语段,起到类组件中 render 方法的效果
每当组件刷新时,都会再次执行函数组件中的语句
生命周期
好,现在发现一个很重要的问题——函数组件没有生命周期钩子函数!
但是 react 作者怎么可能没有想到这个呢?早就给你安排了模拟了
useEffect
首先来了解一下 useEffect
函数,函数原型如下
1 | useEffect(fn[,target]) |
第一个参数是监听的回调函数
第二个参数可不填,表示监听组件中的所有项目,或填入一个数组,数组中的每个元素都是监听的对象
一个用例如下
1 | useEffect(()=>{ |
则每当 n 变化时,都会输出 render run
模拟 constructor
构造器不需要模拟,在函数组件中,return 前的代码都可以认为是构造器
模拟 componentDidMount
根据上述定义,显然我们可以选择填入一个空数组,表示不需要监听任何变量
1 | useEffect(()=>{ |
好了,现在只有函数组件初始化的时候会执行这段输出
模拟 componentWillUnmount
上面说到 useEffect
的第一个参数应该是一个函数,我们可以通过返回值来控制组件消亡前的动作
1 | useEffect(()=>{ |
如上,return 一个函数即可,该函数将在组件即将消亡时被调用
模拟 componentDidUpdate
在如下这个例子中
1 | useEffect(()=>{ |
我们发现它会在 n 变动时打印输出,但在初始化的时候也打印了输出
如果要求不太高的话,其实已经可以就这样满足了
但我们怎么可以这么容易满足呢?我就是要让它在初始化的时候不打印,在更新的时候才打印!
那么容易想到使用一个控制变量来判断是不是第一次渲染
1 | let flag = false |
但是一运行,发现一直都只能打印 first render,怎么回事??
因为直接创建的变量并不会绑定到函数组件上,我们必须通过 useState
函数来创建才可以
那么可以得到如下改版
1 | const [flag, setFlag] = useState(false) |
现在可以正常实现我们的功能了,但是代码好丑
于是我们把这个代码段包装成一个函数
1 | const useUpdate = dep => { |
剥离与组件中变量的依赖,改为只依赖传入的参数
然后在函数组件中通过 useUpdate(n)
进行调用
注意,此处的函数名必须满足 \use.+\
的格式,否则 react 会报错,认为这不是一个 react 组件或函数
但是还是不够灵活——我想传入我自己的处理函数,怎么办呢?
那么我们往这个函数中传入自己的 function
1 | const useUpdate = (fn, dep) => { |
在函数组件中通过 useUpdate(fn, n)
进行调用即可
现在就还挺好看了,用法也和原生 useEffect 挺像
现在把这段逻辑抽离到单独的文件中,然后引入它
1 | // useUpdate.js |
之后就可以在任意文件中引入它了
注意,此处一般建议将 fn 和 flag 都放入监听数组中,否则 react 担心 fn 和 flag 一旦变化,会导致执行结果脱离预期,所以会引发警告(虽然警告一般都不重要)
总结
以上就是 react 函数组件的主要内容
结合类组件的内容,可以看出,react 在绝大多数地方都是推荐使用函数而不是直接赋值,并且大部分时候是通过组合各种功能来实现新功能,而不是依赖继承
如果用不习惯的话,vue 也不错,2333
感谢阅读