模仿 big-react,使用 Rust 和 WebAssembly,从零实现 React v18 的核心功能。深入理解 React 源码的同时,还锻炼了 Rust 的技能,简直赢麻了!
代码地址:https://github.com/ParadeTo/big-react-wasm
本文对应 tag:v25
在 React 新版本中,Suspense
一个非常大的作用就是结合 use
来获取数据,今天我们来实现一下,本次改动见这里。
我们用这个例子来解释一下本次改动:
1 | import {Suspense, use} from 'react' |
我们先按照之前新增 hooks 的流程把相关代码都加上,最后会来到 fiber_hooks.rs
:
1 | fn _use(usable: JsValue) -> Result<JsValue, JsValue> { |
从代码可以看到 use
这个函数即可传入一个 Promise
对象,也可传入一个 Context
对象,这里暂时只讨论 Promise
对象,所以我们看看 track_used_thenable
:
1 |
|
中间的部分先略过,最后会返回一个 Result
的变体 Err
,里面的 payload 为 SUSPENSE_EXCEPTION
,这个 SUSPENSE_EXCEPTION
会在构建的时候插入到结果之中:
1 | const SUSPENSE_EXCEPTION = new Error( |
这里不直接返回 thenable
而是返回 SUSPENSE_EXCEPTION
是为了后续好区分用户代码抛出的异常和 react 自己的异常,我们真正关心的值存在 SUSPENDED_THENABLE
里面。
之后,会来到 work_loop.rs
这里:
1 | loop { |
这个 e
就是前面所说的 SUSPENSE_EXCEPTION
,来看看 handle_throw
是怎么处理的:
1 | fn handle_throw(root: Rc<RefCell<FiberRootNode>>, mut thrown_value: JsValue) { |
这里会判断异常是不是 SUSPENSE_EXCEPTION
,如果是的,就把真正的值重新拿出来,这就跟前面说的对上了。
这个值最后会传给 throw_and_unwind_work_loop
:
1 | loop { |
这个我们上篇文章已经介绍过了,这里就不啰嗦了。我们再回到 track_used_thenable
:
1 | pub fn track_used_thenable(thenable: JsValue) -> Result<JsValue, JsValue> { |
这里首次进来会走 else
,核心逻辑就是给 thenable
添加 on_resolve
和 on_reject
方法,修改它上面的 status
,value
和 reason
属性。
等到 Promise
对象的状态不再是 pending
后,会触发重新渲染,当再次来到这个函数时,它的 status
上也有值了,此时会进入 if
:
1 | if status.is_string() { |
如果其状态为 filfilled
,就返回 value
的值,否则抛出 reason
上的异常。
Suspense
结合 use
hook 获取数据的实现就介绍到这,不过调试发现 bailout 的逻辑会影响该流程的正常工作,所以目前只能暂时注释掉这一部分的代码,后面有时间再来看看如何解决。