前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >尝试 React 17 RC / Demo of Gradual React Upgrades

尝试 React 17 RC / Demo of Gradual React Upgrades

作者头像
张宗伟 plus
发布2022-10-28 13:37:37
6570
发布2022-10-28 13:37:37
举报
文章被收录于专栏:张宗伟 plus张宗伟 plus

背景介绍

前一段时间,React团队发布了 React 17 RC [1],对于这个版本,官方说的是没有新特性,可以称作是一个 “垫脚石” 版本,为以后的版本更新做准备。主要是因为之前的 “all-or-nothing” 升级策略遇到了问题:一方面React团队要一直维护老旧的并且使用较少的API;一方面开发者在面对React版本升级时,往往需要升级整个项目,这意味较高的风险,特别对于很老旧的项目(哈哈,估计到时候很多人都会吐槽~)。所以提供了一个 渐进升级 的方案,那 React 17 就是使得 渐进升级 变得更加容易!为此还更改了 React 的事件代理模式。这篇文章是对官方提供的 渐进升级 的例子 Demo of Gradual React Upgrades [2],表述一下自己认为它是如何工作的。

Part 0

首先从目录说起,src目录下:主要目录有三个

  • modern:使用新版本的React,并且具有独立的 package.json 用于安装 modern 下需要的依赖;
  • legacy:使用老版本的React,并且具有独立的 package.json 用于安装 legacy 下需要的依赖;
  • shared:modern 和 legacy 都使用的组件、context、hooks,格外注意的是 shared 里面的内容会被分别复制到 modern、legacy 的 shared 目录中。
  • 外层的文件中除了 index.js 是 入口文件,还有一个 store.js 存放 redux 中的 store。

那么对于新建的文件或者目录应该怎么存放呢?其实从目录的划分就可以看出,要将项目中使用新版本 React 的部分(modern)和老版本 React (legacy)的部分隔离开,所以呢只在 modern 中使用的放入 modern,legacy 同理;modern 和 legacy 都需要使用的放入 shared;对于一些不涉及到 React 版本的内容直接放入 src 根目录即可。

part 1

这个例子中 React tree 的嵌套模式是新版本嵌套老版本,实现了 context、react-router、redux 的共用,那么如何实现上述三者的共用呢?其实按照正常的思维当然是都来源于一个即可,没错这个例子就是这样实现的。主要涉及的文件为(主要关注点在于如何实现共用的代码,其余的部分不做说明):

  • modern / lazyLegacyRoot.js
代码语言:javascript
复制
/** 
* 注意这是重点,这里导入了 Themecontext,react-router、react-redux 的 context
* context 中存放的就各自的对象信息
*/
import {__RouterContext} from 'react-router';
import {ReactReduxContext} from 'react-redux';
import ThemeContext from './shared/ThemeContext';

// 懒加载 legacy root component
const createLegacyRoot = readModule(rendererModule, () =>
 import('../legacy/createLegacyRoot')
).default;
// 懒加载 legacy children component
const Component = readModule(componentModule, getLegacyComponent).default;
const containerRef = useRef(null);
const rootRef = useRef(null);
// Populate every contexts we want the legacy subtree to see.
// Then in src/legacy/createLegacyRoot we will apply them.
// 获取每个 context 里的信息
const theme = useContext(ThemeContext);
const router = useContext(__RouterContext);
const reactRedux = useContext(ReactReduxContext);
/**
* 组成一个 context 对象,拥有 theme、router、reactRedux 属性
* 并分别赋值为上述的 context 信息
*/
const context = useMemo(
  () => ({
    theme,
    router,
    reactRedux,
  }),[theme, router, reactRedux]);
  
// Create/unmount.
useLayoutEffect(() => {
  if (!rootRef.current) {
    rootRef.current = createLegacyRoot(containerRef.current);
  }
const root = rootRef.current;
  return () => {
    root.unmount();
  };
}, [createLegacyRoot]);

// Mount/update.
useLayoutEffect(() => {
  if (rootRef.current) {
    // 将 context 注入到 需要使用 context、react-router、react-redux 的组件中去
    rootRef.current.render(Component, props, context);
  }
}, [Component, props, context]);
  • legacy / createLegacyRoot.js
代码语言:javascript
复制
/**
* 注意这里是重点,这里导入的对象用于 Provider,将 context 注入到被包裹的组件中
* 这样在组件中就可以使用 ThemeContext、react-router、React-Redux
*/
import ThemeContext from './shared/ThemeContext';
// Note: this is a semi-private API, but it's ok to use it
// if we never inspect the values, and only pass them through.
import {__RouterContext} from 'react-router';
import {Provider} from 'react-redux';

// 进行 Provider 注册 context
<ThemeContext.Provider value={context.theme}>
    <__RouterContext.Provider value={context.router}>
    {/*
      If we used the newer react-redux@7.x in the legacy/package.json,
      we woud instead import {ReactReduxContext} from 'react-redux'
      and render <ReactReduxContext.Provider value={context.reactRedux}>.
    */}
    <Provider store={context.reactRedux.store}>{children}</Provider>
  </__RouterContext.Provider>
</ThemeContext.Provider>

上述代码说明了如何实现 context、react-router、react-redux 的共用,最核心的方式就是使用 Provider 注册 context,让我比较疑惑的是 react-router、react-redux 竟然也有 context,猜测它们内部实现就用到了 context。

part 2

好了,上面既然 Provider 了各自的 context,那么就可以使用了:

  • legacy / Greeting.js
代码语言:javascript
复制
// 消费 ThemeContext
<ThemeContext.Consumer>
  {theme => (
    <div style={{border: '1px dashed black', padding: 20}}>
      <h3>src/legacy/Greeting.js</h3>

      <h4 style={{color: theme}}>
        This component is rendered by the nested React ({React.version}).
       </h4>
       <Clock />
        <p>
          Counter: {this.props.counter}{' '}
          <button onClick={() => this.props.dispatch({type: 'increment'})}>
            +
          </button>
        </p>
        <b>
          <Link to="/">Go to Home</Link>
        </b>           
      </div>
  )}
 </ThemeContext.Consumer>

总结

通过这个例子,大致了解了项目渐进迁移所使用的方式,可以为以后的项目迁移作准备,上面写的正是我理解时间最长的部分,我认为也是关键的部分,虽然有些简单。更多具体信息请查看 参考链接。

参考链接

[1]:https://reactjs.org/blog/2020/08/10/react-v17-rc.html

[2]:https://github.com/reactjs/react-gradual-upgrade-demo/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 张宗伟 plus 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档