本文列举了一些个人认为经典的文章,可以加深对react原理,以及如何正确使用react的理解。很多文章基本就是Dan的blog扒出来,如果有时间,建议大家直接读Dan的blog。
原理相关
react as a ui runtim
运行时讲解react如何工作,不太深,算是一个概览。mark个有意思的点,是我之前也没有考虑过的。 JSX使得惰性求值成为可能,本身也是依赖倒置的易用体现(react帮我们createElement,做协调)。
从源码深入探究React 运行时优化方案的演进
经典好文,没事可以复习一下。15-6-7-8,react到底升级了什么,想要从哪些方面提升体验。
Why Do React Hooks Rely on Call Order?
很经典的面试题,解释了为什么react不能在条件或循环语句中调用。但是注意,这不是从实现(链表)的角度来解释的,而是解释其他方案为什么行不通,所以才有了链表这样的设计。
怎么用好react
其实下面大部分内容,都应该是怎么用好hooks,简而言之:UI=f(data)心智模型。
每一次render,react都是根据当前的props和state生成期望的react tree,然后再做tree到dom的同步,useEffect也是类似,只不过是同步到react tree之外的东西罢了。
React hooks FAQ
React hooks FAQ回答了很多我们常见的开发问题,值得一看。比如怎么拿到上一次渲染的props或者state。
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count); return <h1>Now: {count}, before: {prevCount}</h1>;
}
function usePrevious(value) { const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
A Complete Guide to useEffect
建议反复看,时不时复习一下。关键就是,使用Class components和Function Component是两种心智模型。“think in effects”,它的心智模型更接近于实现状态同步,而不是响应生命周期事件。 以下是几个重要的结论:
-
React 会根据我们当前的props和state同步到DOM 。 “mount”和“update”之于渲染并没有什么区别。我们应该以相同的方式去思考effects。
useEffect使我们能够根据props和state同步React tree之外的东西。 -
effect是在渲染完成后再执行的,包括清理。所以,执行时机是:
- React 渲染
new的UI 。 - 浏览器绘制。我们在屏幕上看到
new的UI。 - React 清除
old的effect。 - React 运行
new的effect。
- React 渲染
-
当我们写类似
setSomething(something => ...)这种代码的时候,也许就是考虑使用reducer的契机。reducer可以让我们把组件内发生了什么(actions)和状态如何响应并更新分开表述。
- 为什么useReducer是Hooks的作弊模式?
function Counter({ step }) {
const [count, dispatch] = useReducer(reducer, 0);
function reducer(state, action) {
if (action.type === 'tick') {
return state + step; } else {
throw new Error();
}
}
useEffect(() => {
const id = setInterval(() => {
dispatch({ type: 'tick' });
}, 1000);
return () => clearInterval(id);
}, [dispatch]);
return <h1>{count}</h1>;
}
Your Guide to React.useCallback()
几种典型的可能需要使用useCallback的场景:
-
包装在
React.memo()(或shouldComponentUpdate)中的组件接受回调prop。 -
当函数用作其他hooks的依赖项时
useEffect(...,[callback])。
在使用useCallback前,可以先用profile看看能带来多大的性能优化,关于useCallback的效果可以看看下面这个demo。
Making setInterval Declarative with React Hooks
直接上结果,本质其实是命令式到申明式的转换adapter。
import React, { useState, useEffect, useRef } from 'react';
function useInterval(callback, delay) {
const savedCallback = useRef();
// 保存新回调
useEffect(() => {
savedCallback.current = callback;
});
// 建立 interval
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
可以思考一个小问题,下面的改动会导致什么结果?
import React, { useState, useEffect, useRef } from 'react';
function useInterval(callback, delay) {
const savedCallback = useRef();
// 保存新回调
useEffect(() => {
savedCallback.current = callback;
});
// 建立 interval
useEffect(() => {
// function tick() {
// savedCallback.current();
// }
if (delay !== null) {
let id = setInterval(savedCallback.current, delay);
return () => clearInterval(id);
}
}, [delay]);
}
How to fetch data with React Hooks
由简入繁,层层递进,通过不断演进fetch数据的需求,讲解怎么正确地使用hook fetch数据。
Before You memo()
除了使用memo,通过简单的组件拆分,也能达到优化的目的:
-
state下沉。
-
JSX as children。
Writing Resilient Components
编写弹性(可伸缩)的组件,基本含义就是可复用。
-
不要阻断数据流 —— props 和 state 可能会更新,组件应该处理好这些更新。
-
不要把可以从props推导的属性放到state中,因为props变了,state是不会更新的。
- 如果props的值是state状态的初始值,用明确的语义标注,比如defaultValue
- 如果推导计算很重,那就memo。
-
不要在Side Effects里面阻断数据流。
- props 和 state 是 React 数据流的一部分。在这个数据流中,rendering 和 side effects 都应响应它们的变化,而不是忽略它们!
- useEffect API 显式封装了依赖,可以避免依赖更新后,副作用不再执行的问题。
-
不要在优化中阻断数据流。
- 如果尝试通过编写自己的比较方法来 “优化” 组件,我们可能会错误地忘记比较函数属性
- FC的中定义的function在每次render都不同,不用担心ClassComp中method依赖的数据变化,但是因为method本身不变,而导致子组件不重新渲染的问题。通过
useCallback和useContext可以避免渲染的问题。
-
-
时刻准备渲染 —— 一个组件不应该被或多或少的渲染而损坏。
-
没有单例组件 —— 即使组件只渲染一次,但通过设计让它渲染两次也不会被破坏。
-
隔离本地状态 —— 想想哪个状态是特定 UI 展示下的本地状态——并且除非必要,不要将该状态提升到更高的地方
- 如果我们不确定某个状态是否属于本地,请问自己:“如果此组件呈现两次,交互是否应反映在另一个副本中?” 只要答案为“否”,那我们就找到本地状态了。