React / Context API / TypeScript:如何初始化 Web 应用程序并避免 UI 在启动时闪烁?

React / Context API / TypeScript : How to init web app and avoid UI flickering at statup?

当用户导航到我的站点时,我需要按如下方式初始化 React 网络应用程序:

  1. 如果传入用户从未请求过 Web 应用程序,我想将 UI 语言设置为浏览器默认语言 (navigator.language)。

  2. 如果传入用户已经访问过该站点并选择了首选语言(lang 存储在 localStorage 中),我想用这种语言初始化 UI。

  3. 如果传入用户有一个帐户并且已经连接(令牌在 localStorage 中可用),我想自动连接他并相应地呈现应用程序:登录按钮转换为欢迎消息,UI 语言设置为用户首选项。

为此,我使用了 React Context API 和一个 defaultUser 对象。

defaultUser: 初始化默认用户

const defaultUser = {
  language: 'en_EN',
  isConnected: false
}

Context: 创建默认上下文

export const AppContext = createContext({
  connectedUser: defaultUser,
})

Provider: 使用默认上下文创建提供程序

export function AppProvider({ children }: any) {

  [...]

  const provider = {
    connectedUser
  }

  return (
    <AppContext.Provider value={provider}>
      {children}
    </AppContext.Provider>
  )
}

App: 在应用程序启动期间初始化提供程序

export class App extends Component {

  static contextType = AppContext

  render() {
    return (
      <AppProvider>
        <AppContainer />
      </AppProvider>
    )
  }
}

AppContainer: 呈现应用程序

export class AppContainer extends Component {

  static contextType = AppContext

  componentDidMount() {
    /** If user is not connected, verify if a stored session exists and use it to connect user */
    if (!this.context.connectedUser.isConnected) {
      [...do things...]
    }
  }

除烦人的事情外,整个机制运行良好:网络应用程序使用默认用户值系统地初始化,直到 AppContainer::componentDidMount() 完成真正的初始化工作。 这导致了一种闪烁效果。

我为如何解决这个问题苦苦挣扎了 2 天,试图在 <AppContainer /> 渲染之前执行 Context 初始化,但我被卡住了。

有什么建议吗?

编辑: 为了清楚起见,我添加了一个图表。目前:

  1. React 应用程序呈现在 start
  2. 上下文在 start 初始化为默认值。
  3. 达到 end 时更新上下文。
  4. React App 在 end.
  5. 时再次渲染

这两个步骤中的任何布局更改(UI 语言,UI 基于用户权限的修改)对用户来说清晰可见,并产生一种闪烁。

我通过简单地调节 <AppContainer/> 加载找到了一种解决方案,将其推迟到序列的末尾。然而,我现在没有闪烁,而是有滞后和其他不需要的副作用。

目标是在呈现 React Act 之前和 Window 可用之后改变所有序列。然后动态创建Context,然后render all.

我认为如果我可以在 App constructor()componentWillMount() 期间动态创建 AppContext 并将变量传递给 createContext()(不确定何时 Window 可用),但 TypeScript 开始处理类型问题,我仍然卡住了。

您没有共享初始化上下文的代码,但我怀疑您将默认值设置为硬编码值或 navigator.language,因此遇到了闪烁。我想建议两种方法来解决这个问题:

解决方案 1

也许您可以通过访问 localStorage.get('lang') 或类似方式以编程方式生成默认上下文,而不是使用硬编码的默认上下文?但是这个解决方案有一个小缺点:你会混合对反应和浏览器的关注,但我认为在这种情况下它是一个可以考虑的替代方案,因为它对 reader.[=14 来说非常简单和明显。 =]

解决方案 2

或者,在调用 ReactDOM.render 时,您可以将 localStorage 中的任何内容作为 prop 传递给您的应用程序,这样您就可以将浏览器相关逻辑与纯 React 内容分开。

我希望这是有道理的。

这是我在 之后的跟进,以防对其他人有所帮助。

使用函数

初始化上下文

我没有使用硬编码值初始化 defaultUser 并稍后更新它,而是直接使用返回 navigator.lang 的函数按照建议进行设置。这解决了 UI 标签上的闪烁问题。

RectDOM.render

之前初始化数据

但是我仍然在 UI 组件上闪烁,我必须从 API 调用中获得适当的状态。

例如,如果传入用户在 localStorage 中存储了有效的会话令牌,则必须禁用 Login 按钮。在这样做之前,我需要通过对 API 的异步调用来确保会话令牌有效。我没有找到让 Context 初始化“等待”它的方法,它似乎是同步的。

这就是 Amit 第二个建议发挥作用的地方。我没有在 React 中努力寻找解决方案,而是在 ReactDOM.render 之前进行了必要的处理,然后将东西作为道具传递给 <Apps/>

这对于获取和传递数据非常有效...

除了 Context API 不再 setSate 一旦它的任何数据引用来自 [=41] 之外的对象=]上下文。换句话说,使用函数调用可以初始化(可能通过 val),但对外部对象的引用会中断 setState.

结论

由于我的项目仍处于早期阶段,这让我有机会摆脱 Context API,根据需要进行适当的初始化,并对props/states 使用基本 React 传播。