likes
comments
collection
share

React Context的核心思路为什么使用Stack?

作者站长头像
站长
· 阅读数 13

大家好,我是梦兽编程。更多知识专栏关注梦兽编程梦兽编程

在软件工程中,编码只是一个环节。在软件工程中可不是只有编码这么简单的,在还没前后分离的年代中,很多做程序都明白说什么。

不知道什么时候开始“面试造火箭,工作牛螺丝”的名言,市场那有这么多高并发的项目,其实很多企业也没认清业务就盲目选技术栈的,比如微前端,微服务,明明不需要的有多少企业是硬上的?程序员主要不是你懂多少八卦文。程序员的核心加载是编码思路和遇到问题的解决方案,比如一个场景中能给出什么解决方案。

所以业务带动技术发展也是这么来的,因为技术本来就是为了解决问题诞生的

所以我们能真正去理解透一个项目是如何闭环,弄清楚其中的因果关系,不要因为背八卦文而背八卦文。

React Context 的核心思路是什么?

今天梦兽编程要分享的是React Context的核心思路,我们在遇到一个问题需要研究的时候,往往可以通过两种形式进行切入。

  1. 先观察问题的起因出发考虑,
  • 比如你可以考虑为什么需要cors?
  • 如今json web token的年代还需要cors嘛?
  • 为什么cors可以保护网站安全?它实际保护了啥?
  1. 从结果出发,直接看这个事物客观反应的结果
  • 市面上的大多数产品经理不就是借鉴竞品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);
}

我们很容易看到ReactFiberStackStack的命名。我们在将上面的代码改造一下就能形成一下代码。

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 排版