React Context的核心思路为什么使用Stack?
大家好,我是梦兽编程。更多知识专栏关注梦兽编程梦兽编程。
在软件工程中,编码只是一个环节。在软件工程中可不是只有编码这么简单的,在还没前后分离的年代中,很多做程序都明白说什么。
不知道什么时候开始“面试造火箭,工作牛螺丝”的名言,市场那有这么多高并发的项目,其实很多企业也没认清业务就盲目选技术栈的,比如微前端,微服务,明明不需要的有多少企业是硬上的?程序员主要不是你懂多少八卦文。程序员的核心加载是编码思路和遇到问题的解决方案,比如一个场景中能给出什么解决方案。
所以业务带动技术发展也是这么来的,因为技术本来就是为了解决问题诞生的。
所以我们能真正去理解透一个项目是如何闭环,弄清楚其中的因果关系,不要因为背八卦文而背八卦文。
React Context 的核心思路是什么?
今天梦兽编程要分享的是React Context的核心思路,我们在遇到一个问题需要研究的时候,往往可以通过两种形式进行切入。
- 先观察问题的起因出发考虑,
- 比如你可以考虑为什么需要cors?
- 如今json web token的年代还需要cors嘛?
- 为什么cors可以保护网站安全?它实际保护了啥?
- 从结果出发,直接看这个事物客观反应的结果
- 市面上的大多数产品经理不就是借鉴竞品copy出来的需求?
- 看结果导向猜,这就有点像高数数学有一道数学题叫找规律。梦兽以前做这道题往往都是从结果往上推到解出来的。
我们先看一下React中createContext
是如何实现的。
// 精简过core
function createContext(defaultValue) {
const context = {
$$typeof: REACT_CONTEXT_TYPE,
Provider: null,
_currentValue: defaultValue
};
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context
};
return context;
}
有些人在这里会有个好奇点在 _context: context
到底会不会循环引用?梦兽这里给出的答案是不会。但是如果写明的代码时会出现循环引用的。
// Context对象
const context = {
Provider: null,
};
// Provider直接引用Context对象
context.Provider = {
_context: context
};
// 循环引用
context.Provider._context = context;
而React 的代码中添加了_currentValue
属性就就很完美的避开了这个问题。这个是一个js很古老的问题知识点了,多谢ES6吧,让现在的程序员轻松不少。
// Context对象
const context = {
_currentValue: defaultValue,
Provider: null,
};
// Provider引用的是Context对象的引用
// 将 Context 对象的引用赋值给 Provider 的 _context 属性
context.Provider = {
_context: context
};
// 没有循环引用
context.Provider._context._currentValue = defaultValue;
// 感兴趣的可以试试 输出什么内容
context.Provider._context === context
有了createContext
我们进行简单的使用后发现。这里要非常注意我打注释的地方
const ctx = createContext(0);
function App() {
return (
// 手写我们必须要知道机器在read的过程是从上往下执行的
// 所以我们的 Contxt 的value 是
// 1
// 2
// 3
// 这个顺序记录起来,那我们咱们实现的过程结果是怎样的呢?
// jsx可以看作成fp的方法调用
// 所以这里的执行顺序应该是这种的 fn1(fn2(fn3))
// 按照函数范式编程规范中这里的result的先执行fn3->fn2->fn1
<ctx.Provider value={1}>
<ctx.Provider value={2}>
<ctx.Provider value={3}>
// 3
<Com />
</ctx.Provider>
// 2
<Com />
</ctx.Provider>
// 1
<Com />
</ctx.Provider>
);
}
function Com() {
const num = useContext(ctx);
return <div>{num}</div>;
}
以上代码我们可以看到浏览器页面中会呈现出321
的结果。在软件工程中,我们会把这种后进先出的特性叫做Stack
。
在React16后我们都知道React更换了fiber
算法,而我们的use hooks也是挂载到fiber
中,所以我们很容易去到[react-reconciler](https://github.com/facebook/react/tree/main/packages/react-reconciler/src)
找到相关实现。
来的 ReactFiberNewContext.ts
文件夹。
在这个文件中我会看到几个关键词
import {createCursor, push, pop} from './ReactFiberStack';
export function pushProvider<T>(
providerFiber: Fiber,
context: ReactContext<T>,
nextValue: T,
): void {
if (isPrimaryRenderer) {
push(valueCursor, context._currentValue, providerFiber);
context._currentValue = nextValue;
} else {
push(valueCursor, context._currentValue2, providerFiber);
context._currentValue2 = nextValue;
}
}
export function popProvider(
context: ReactContext<any>,
providerFiber: Fiber,
): void {
const currentValue = valueCursor.current;
// ...
pop(valueCursor, providerFiber);
}
我们很容易看到ReactFiberStack
中Stack
的命名。我们在将上面的代码改造一下就能形成一下代码。
const prevContextValueStack = [];
// 这里偷懒了就不在找一个存放prevContextValue写在全局
let prevContextValue = null;
function pushProvider(context, newValue) {
// 实际在写<Provider xxx>的时候执行
prevContextValueStack.push(prevContextValue);
prevContextValue = context._currentValue;
context._currentValue = newValue;
}
function popProvider(context) {
// fn 执行函数的时候 useContext 进行取值
context._currentValue = prevContextValue;
prevContextValue = prevContextValueStack.pop();
}
这就是Context的实现思路,梦兽在这个过程是从fp的执行顺序导向进行分析为什么这个ReactFiberStack
使用的Stack
。而不是队列Queue
或者其他数据结构。
总结
如果开发中,解决方案的思路才是软件的核心,我们叫它为算法。不要小看你平时写的if else或者crud如果你真的认真想象一个项目如何闭环,想想为什么这样,你会有不一样的提升。数据化转型的未来需要的是更多能做事的人,你能把你做过项目很清晰的说出来。项目中如何解决问题,给出了什么解决方案。
程序员需要的创造力和解决方案的能力,而不是在那里卷几个api...全栈工程是一个很好锻炼软件思维的,如果感兴趣可以定义rexweb.link/categories/…进行next.js全栈学习,然后进行nest.js,golang等,到最后可以自己站在软件工程的角度思考问题。
点击链接加入群聊梦兽编程交流区-带飞
我的B站视频号更多视频动态。
本文使用 markdown.com.cn 排版
转载自:https://juejin.cn/post/7278575483909439499