前言
最近都在学习React、React-Router、Redux(包括umi、dva什么的),当然还有憋得我很难受的论文。相比起憋论文,我觉得写代码实在太有意思了。
话题跑远了,总之学了这段时间,对React的设计模式啊、Redux的使用啊(说来惭愧,我上手Redux还是从dva入的门)、React-Router的一些实践啊(其实相比起react-router-config配置静态路由写路由表,我其实更喜欢写哪里都有的动态路由)都有了一些心得。然后现在就准备把这些心得写一写,加深印象。
就先从函数式组件和类组件的孽缘说起吧~
函数式组件与类组件的不同
函数式组件与类组件的不同有很多,给人最直观的感受可能是写法不同。最核心的不同大概是设计理念不同,毕竟函数式只关注输入和输出。由此延伸出来,实际上最大的不同就是状态的不同(这个不好总结,还是看下面分析吧)。
异步过程中的生化感染
举个实际的例子,假设你现在是小A,你点击了小B的头像,跳转到了小B的空间,此时你的状态还是小A,你可以做的事就是给小B点赞、评论。
这是个很常见的场景,但是这里面包含了一堆异步过程,在异步过程中,我们绝对不希望状态发生改变——要是点了小B的头像,跳到他的空间你就变成了小B,啊,那也太恐怖了(十分不符合常理,跟生化感染似的)。
通常情况下,我们会希望异步函数的参数还是原来的那个值,这样就会得到我们所预期的结果(当然,也有我们想一直获取最新值的时候)。此时,用函数式组件是很好的选择。
React中的函数总是捕获他们的值
函数式里没有this这个东西(你一般只在写类组件时用到this),函数只关心当前的输入和输出。只要输入值确定,输出值就确定,不会改变。
这里说点题外话,对于函数式编程(Function Programing),我是结合AOP(面向切面编程) 理解的。每个函数处理部分功能,接受输入,固定输出,每个函数都能作为下一个函数的输入。这么一个切面上有很多函数,只要你给出输入,就能获得最终你想要的结果,而不用思考该怎么组合。函数只需要你告诉他怎么做,再给出参数就行,而不用手动去操作。
有点扯远了,说回正题。函数式组件传入props,那么此时(或者说本次渲染调用本次函数)函数里的props就是传入时的值,输入值固定。那么在本次渲染时,输出值也将是固定的,不会改变。
函数式组件被证明是一个好的选择,那么使用类组件呢?这两类组件到底有什么差异呢?
当你使用类组件……
在React里,props和state都是immutable(不可变) 的,这是很好的一点。但是,this是可变的,且是永远都在变化的。
因此类组件会存在延时后读取props值发生改变的情况。你要是想获取之前的props值,就得想想其他办法,比如:
const { user } = this.props;
...
setTimeout(() => this.update(user), 3000);
要么在构造函数里绑定方法(“危”),如:
constructor(props) {
super(props);
this.update = this.update.bind(this);
}
但是上面两种方法都是有缺陷的。
对第一种方法来说,代码变得冗长,而且要是调用的参数不止一个,或者需要调用state时,都得写出来。
对第二种方法来说,其实这是并不起效的(hhh)。
当我们从this.props里读取数据时已经太迟了,此时
读取的已经不是我们所需要使用的上下文了。
此时,我们需要一个闭包来解决问题。
大神告诉我们,尽量避免使用闭包——我们难以想象
一个可能会随着时间推移而变化的变量。但是在当前
情景,没有问题,因为在react中的props和state是
immutable的。
使用闭包是没有问题的,只要保证不变。
在每一次渲染时capture就不会改变:
render() {
const props = this.props;
...
}
此时,所有写在render里的函数都将能使用当前capture的props,它并不会改变。
emmmm,尽管问题解决了,但是问题也没有解决。
你看,这个Class这么没用,不如我们……
你要是把所有函数都写在render里,那写个class有啥意义呢??
(人类迷惑行为)
既然我们不需要class了,那我们就把class删了吧
然后就变成了函数式组件(哈哈哈)。
(人类迷惑行为大赏)
总结下,就是:函数式组件捕获了渲染所使用的值,而类组件时刻在改变。
假如我就要最新的值呢?
拓展下,假如你想要在函数式组件里使用最新的值怎么办呢?
这个问题在类组件里并不是问题,因为this.props和this.state的this随时可变。
不过在函数式组件里,你可能得用上“ref”。
比如使用useRef这个hook:
const ref = useRef(null);
我自己有封装了个通用的钩子函数(大概各位也有):
function useCurrentValue<T>(value: any): React.RefObject<T> {
const ref = React.useRef(null);
ref.current = value;
return ref;
}
这个钩子就是使用现在的值,因为这个还是挺通用的,我就把它包装了一下,扔utlis里了。
通常情况,我们应该避免在渲染期间使用refs,这很好理解:
因为refs是可变的,而我们希望渲染的结果是可预测的,而不是难以控制的。
但是假如我们想要特定值的最新值,而手动更新很烦人,我们可以用另一个hook来实现更新:
const [msg, setMsg] = useState('');
const latestMsg = useRef('');
useEffect(() => {
latestMsg.curret = msg;
});
总结
终于到了总结部分了 = =
也没啥好总结,就随便说两句吧
(得亏我一直很讨厌写类的方式,所以写起函数式很开心,哈哈哈)
写代码时要认为任何值都可以随时更改,指不定什么时候就被坑了。
Over.