Live Note

Remain optimistic

React Fast Refresh 是 React 官方推荐的 HMR 解决方案,旨在提供快速、可靠的热更新体验,同时保留组件的本地状态(例如 useStateuseRef 的值)。它由 Facebook 团队开发,广泛集成到现代构建工具(如 Vite 和 Next.js)中。

背景与目标

传统 HMR 的问题

  • 在传统的 HMR 实现中,当模块更新时,整个组件树可能会被重新渲染,导致本地状态丢失。
  • 对于 React 组件,状态丢失会影响开发体验,例如表单输入值或动画状态被重置。

React Fast Refresh 的目标

  • 提供快速的模块更新。
  • 保留 React 组件的本地状态。
  • 只重新渲染受影响的组件,而不是整个应用。

核心原理

React Fast Refresh 通过在编译时注入特定的运行时代码,并在运行时与 React 协调器(React Reconciler)协作,实现高效的热更新。

Read more »

1. Make Sure the compileSdkVersion is correct.

Cause if you don’t have the correct version of compileSdkVersion, the Context.RECEIVER_EXPORTED will not found.

build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
buildscript {
// ...
ext {
buildToolsVersion = "***"
minSdkVersion = ***
compileSdkVersion = 33
targetSdkVersion = 34
ndkVersion = "***"
googlePlayServicesAuthVersion = "***"
}
// ...
}

2. Rewrite the registerReceiver in MainApplication.java

Make sure the registerReceiver is override before the onCreate function.

MainApplication.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ...
@Override
public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter) {
if (Build.VERSION.SDK_INT >= 34 && getApplicationInfo().targetSdkVersion >= 34) {
return super.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
} else {
return super.registerReceiver(receiver, filter);
}
}

@Override
public void onCreate() {
// some code here
}
// ...

3. Clean Project and Rebuild the apk package

Reference here: React Native App Crashes — On upgrading to targetSdkVersion 34(Android 14)

示例

certificate

main.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import Svg, {
Rect,
Text as SvgText,
Path,
LinearGradient as SvgLinearGradient,
Stop,
Defs,
Line,
} from "react-native-svg";
import BuildingSvg from "./BuildingSvg";
<Svg height="500" style={{ marginHorizontal: 16 }} width={wp(100) - 16 * 2}>
<Defs>
<SvgLinearGradient id="textGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<Stop offset="0%" stopColor="#835927" />
<Stop offset="50%" stopColor="#C08035" />
<Stop offset="100%" stopColor="#835927" />
</SvgLinearGradient>
</Defs>
<BuildingSvg />

<Rect
x="10"
y="10"
width="95%"
height="95%"
stroke="#DAA520"
strokeWidth="5"
fill="none"
rx="10"
ry="10"
/>
<Path
d="M20,40 L360,40 M20,460 L360,460 M40,20 L40,480 M340,20 L340,480"
stroke="#DAA520"
strokeWidth="2"
/>
<SvgText
x="50%"
y="80"
fontSize="20"
fontWeight="bold"
textAnchor="middle"
fill="url(#textGradient)"
>
Certificate of Honor
</SvgText>
<SvgText x="50%" y="120" fontSize="16" textAnchor="middle" fill="#835927">
Proudly Persented to
</SvgText>
<SvgText
x="50%"
y="180"
fontSize="24"
fontWeight="bold"
textAnchor="middle"
fill="#835927"
>
Edward
</SvgText>
<Line x1="35%" y1="184" x2="65%" y2="184" stroke="#835927" strokeWidth="2" />
<SvgText x="50%" y="250" fontSize="18" textAnchor="middle" fill="#835927">
We acknowledge your dedication
</SvgText>
<SvgText x="50%" y="270" fontSize="18" textAnchor="middle" fill="#835927">
and applaud your success as
</SvgText>
<SvgText x="50%" y="290" fontSize="18" textAnchor="middle" fill="#835927">
a distinguished trader
</SvgText>
<SvgText x="50%" y="310" fontSize="18" textAnchor="middle" fill="#835927">
in the cryptocurrency market.
</SvgText>
<SvgText
x="50%"
y="450"
fontSize="16"
textAnchor="middle"
fill="url(#textGradient)"
>
NO. 10020123
</SvgText>
</Svg>;
Read more »

起因

如果 wrapp 的 flex-direction 为 row 的时候,Text 内部不自动 wrap。还是测试拿给我看,才发现某些机型会存在这种问题。

开始以为是 Text 内部会根据flexbox而有不同的展现,但是翻阅官方文档:

The element is unique relative to layout: everything inside is no longer using the Flexbox layout but using text layout. This means that elements inside of a are no longer rectangles, but wrap when they see the end of the line.

Text 组件内部默认是 wrap 的,除非这一整个是一个超长的单词,否则就会在 white space 的时候自动 break 了。
如果给 wrapp 设置 wrap,那么整个 text 直接就 wrap 到下一行,icon 单独一行了。(这部分官方是有案例的,根据 Text 外部的 Container,wrap 也是不一样的)

好嘛,到底是什么东西导致的?为什么 flex-direction: row 就给我炸了?

原因: element 的宽度计算方式导致的

解决

官方的解释:每个 element 的 size 大致可以分为两种方式:

  • content-driven
  • parent-driven
  1. flex-direction: column 的时候,宽度首先由 content-driven,然后再由 parent-driven,高度直接由 content-driven,完美,一点问题没有。
  2. flex-direction: row 的时候:高度由 comtent-driven,宽度呢?那当然是 content-driven 啊! … 不好意思,Text 无法计算自己的 width,why?因为 Text 是 autosize 的!(也有意外情况,如果你把 Text 给 absolute 起来,那么就可以计算了)。所以此时的 Text 的 width,是由 parent-driven,也就是整个 wrapper 的宽度了。

解决方法也很简单,Text 上挂一个 flexShrink: 1,让它自己玩去吧。

  1. 从 Reflect 对象上可以获得语言内部的方法
  2. 修改某些 Object 方法的返回结果,让其变得更合理。比如 Object.defineProperty 在无法定义属性时会抛出一个错误,而 Reflect.defineProperty 则会返回 false
  3. 让 Object 操作都变成函数行为。
  4. 只要是 Proxy 对象的方法,就能在 Reflect 对象上找到相应的方法,无论 Proxy 怎么修改默认行为,总可以在 Reflect 上获取默认行为

静态方法

  • Reflect.apply(target, thisArg, args)
    等同于 Function.prototype.apply.call(func, thisArg, args),用于绑定 this 对象后执行给定函数。

  • Reflect.construct(target, args)
    等同于 new target(…args),提供了一种不使用 new 来调用构造函数的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    function Greeting(name) {
    this.name = name
    }
    //new 的写法
    const instance = new Greeting("张三")

    //Reflect.construct 写法
    const instance = Reflect.construct(Greeting, ["张三"])
Read more »