useState

react对useState进行了封装,调用了mountState。

function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
currentHookNameInDev = 'useState';
mountHookTypesDev();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return mountState(initialState);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
}

mountState

如果initialState是函数还可以执行。

生成一个dispatch方法,通过闭包绑定当前states。

把初始值存到memoizedState上。这个memoizedState绑定到fiber树上。用来存储state。

function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
// 把hooks加入queue,实际上是为了保证执行顺序。
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
const queue = (hook.queue = {
last: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
});
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
null,
// Flow doesn't know this is non-null, but we do.
((currentlyRenderingFiber: any): Fiber),
queue,
): any));
return [hook.memoizedState, dispatch];
}

memoizedState

react其实不知道我们调用了几次useState。

所以还是在memoizedState上动手脚,这个处理体现在mountWorkInProgressHook

 memoizedState: {
baseState,
next,
baseUpdate,
queue,
memoizedState
}

memoizedState.next就是下一次useState的hook对象。

hook1 === Fiber.memoizedState
state1 === hook1.memoizedState
state2 = hook1.next.memoizedState

因为以这种方式存储,所以usestate必须在functionalComponent的根作用域中。不能被for,和if。

setstate

mountState函数返回的是 return [hook.memoizedState, dispatch];

dispatch通过闭包就可以处理state。

更新

useState在更新的时候是调用的updateState,这个函数其实是封装的updateReducer。

function renderWithHooks(){
ReactCurrentDispatcher.current =
nextCurrentHook === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
};
HooksDispatcherOnMount: {
useState: mountState,
}
HooksDispatcherOnUpdate: {
useState: updateState,
}

updateReducer

可以看到updateReducer把新的fiber中的state值更新,返回新的值。然后后续走渲染流程。(之前写过reat 的渲染流程)

还可以看到这有个循环update = update.next; while (update !== null && update !== first);

这就是hooks的batchUpdate。

function updateReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch<A>] {
const hook = updateWorkInProgressHook();
const queue = hook.queue; queue.lastRenderedReducer = reducer; // ...
// The last update in the entire queue
const last = queue.last;
// The last update that is part of the base state.
const baseUpdate = hook.baseUpdate;
const baseState = hook.baseState; // Find the first unprocessed update.
let first;
if (baseUpdate !== null) {
if (last !== null) {
// For the first update, the queue is a circular linked list where
// `queue.last.next = queue.first`. Once the first update commits, and
// the `baseUpdate` is no longer empty, we can unravel the list.
last.next = null;
}
first = baseUpdate.next;
} else {
first = last !== null ? last.next : null;
}
if (first !== null) {
let newState = baseState;
let newBaseState = null;
let newBaseUpdate = null;
let prevUpdate = baseUpdate;
let update = first;
let didSkip = false;
do {
const updateExpirationTime = update.expirationTime;
if (updateExpirationTime < renderExpirationTime) {
// Priority is insufficient. Skip this update. If this is the first
// skipped update, the previous update/state is the new base
// update/state.
if (!didSkip) {
didSkip = true;
newBaseUpdate = prevUpdate;
newBaseState = newState;
}
// Update the remaining priority in the queue.
if (updateExpirationTime > remainingExpirationTime) {
remainingExpirationTime = updateExpirationTime;
}
} else {
markRenderEventTimeAndConfig(
updateExpirationTime,
update.suspenseConfig,
); // Process this update.
if (update.eagerReducer === reducer) {
// If this update was processed eagerly, and its reducer matches the
// current reducer, we can use the eagerly computed state.
newState = ((update.eagerState: any): S);
} else {
const action = update.action;
newState = reducer(newState, action);
}
}
prevUpdate = update;
update = update.next;
} while (update !== null && update !== first); if (!didSkip) {
newBaseUpdate = prevUpdate;
newBaseState = newState;
} // Mark that the fiber performed work, but only if the new state is
// different from the current state.
if (!is(newState, hook.memoizedState)) {
markWorkInProgressReceivedUpdate();
} hook.memoizedState = newState;
hook.baseUpdate = newBaseUpdate;
hook.baseState = newBaseState; queue.lastRenderedState = newState;
} const dispatch: Dispatch<A> = (queue.dispatch: any);
return [hook.memoizedState, dispatch];
}

react 16 Hooks渲染流程的更多相关文章

  1. react 16 ssr的重构踩坑

    ssr 服务端不能识别前端的window.特别是首屏渲染的数据需要用到window对象(比如href += location.search); 服务端不能加载图片,css文件. require.ext ...

  2. react 16 渲染整理

    背景 老的react架构在渲染时会有一些性能问题,从setstate到render,程序一直在跑,一直到render完成.才能继续下一步操作.如果组件比较多,或者有复杂的计算逻辑,这之间的消耗的时间是 ...

  3. React 16.x & Hooks

    React 16.x & Hooks Hooks https://reactjs.org/docs/hooks-intro.html https://reactjs.org/docs/hook ...

  4. React 16 服务端渲染的新特性

    React 16 服务端渲染的新特性 React 16 中关于服务端渲染的新特性 快速介绍React 16 服务端渲染的新特性,包括数组.性能.流等 React 16 终于来了!

  5. react16 渲染流程

    前言 react升级到16之后,架构发生了比较大的变化,现在不看,以后怕是看不懂了,react源码看起来也很麻烦,也有很多不理解的地方. 大体看了一下渲染过程. react16架构的变化 react ...

  6. react 使用hooks

    react hooks文档 λ yarn add react@16.7.0-alpha.2 λ yarn add react-dom@16.7.0-alpha.2 设置 state import Re ...

  7. [译] React 16.3(.0-alpha) 新特性

    原文地址:What's new in React 16.3(.0-alpha) 原文作者:Bartosz Szczeciński 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/ ...

  8. react 16.8版本新特性以及对react开发的影响

    Facebook团队对社区上的MVC框架都不太满意的情况下,开发了一套开源的前端框架react,于2013年发布第一个版本. react最开始倡导函数式编程,使用function以及内部方法React ...

  9. cocos2d-x渲染流程

    Cocos2Dx之渲染流程 发表于8个月前(2014-08-08 22:46)   阅读(3762) | 评论(2) 17人收藏此文章, 我要收藏 赞2 如何快速提高你的薪资?-实力拍“跳槽吧兄弟”梦 ...

随机推荐

  1. 【08】Nginx:安全优化 / 信息隐藏 / 请求限制 / 白名单

    写在前面的话 nginx 中主要的内容在前面的章节其实已经差不多了,接下都是一些小功能的实现以及关于 nginx 的优化问题.我们一起来探讨以下,如何把我们的 nginx 打造成为企业级应用. 安全优 ...

  2. NLP第一课(我也是才开始学)

    闲着无聊的时候,我就会问问自己,编程也有了五年经验了,除了增删改查,我还会什么,有一天我跳槽,去面试的时候,我能比那些年轻而且期望薪资待遇低的年轻毕业生,我有什么优势,而且我只是一个专科的机电系学生, ...

  3. [基础] - 从xx语言是跨平台的说起

    我经常碰到一些人在说xx语言跨平台而yy语言不是(为避免不必要的纷争,在此不写具体语言但不影响阅读),从而来表明自己使用xx语言进行程序开发进而在编程语言鄙视链上高高在上很有优越感. 大概是从Java ...

  4. c# 模拟表单提交,post form 上传文件、数据内容

    转自:https://www.cnblogs.com/DoNetCShap/p/10696277.html 表单提交协议规定:要先将 HTTP 要求的 Content-Type 设为 multipar ...

  5. 【Web安全入门】三个技巧教你玩转XSS漏洞

    XSS漏洞是Web应用程序中最常见的漏洞之一,想要入门Web安全的小伙伴,这个知识点是必学的. i春秋官网中有很多关于XSS漏洞的课程,新手小白可以去官网看课学习. 学习地址:https://www. ...

  6. wpf DATAgrid模板中button 命令绑定以及命令参数绑定

    场景:视频上传功能,上传列表使用DataGrid控件,视频有不同的状态对应不同的操作,DataGrid中最后一列为操作列,里面是Button控件.希望点击Button后执行对应的操作,但是设置Butt ...

  7. Google Analytics 学习笔记四 —— GA的Channels划分规则

    一.流量的Source.Medium和Campaigns 1.Source Source或traffic source通常表示流量的来源,一般通过urm_source参数跟踪 在GA中,流量来源的名字 ...

  8. LeetCode——Employees Earning More Than Their Managers

    The Employee table holds all employees including their managers. Every employee has an Id, and there ...

  9. CVE-2019-0708漏洞利用复现

    CVE-2019-0708漏洞利用复现 距离这个漏洞爆出来也有好几个月了,之前一直忙也没来得及写文档,现在重新做一遍. 准备环境: win7靶机 IP地址:172.16.15.118 Kali攻击机 ...

  10. Rocketmq原理&最佳实践

    MQ背景&选型 消息队列作为高并发系统的核心组件之一,能够帮助业务系统解构提升开发效率和系统稳定性.主要具有以下优势: 削峰填谷(主要解决瞬时写压力大于应用服务能力导致消息丢失.系统奔溃等问题 ...