sanjar-notes/react

escape hatch - out of UI tree 'services', example - window.location in mobile

Opened this issue · 2 comments

I know that

React recommends keeping the UI tree (React lib instance) cognizant of all changes.
This is the primary reason why window.location and such things are generally discouraged if you're using React Router, one should prefer the lib.

This works fine for majority of stuff

Problem - hooks and JS utils function

  1. An app also has many util functions (JSX is absent here, only JS). There's also networking libs, that cannot be part of the UI tree. Example - axios instance.
  2. Hooks - hooks can be called only inside components. So these util functions can never access them (they can if you pass it in components - dependency inversion, but that's too much code generally).

Specific problem (axios interceptor redirects to Screen seems hard)

Redirection in RN apps is a major issue (since window.location is absent), and React Navigaton functions can only be called on registered Screens or via the hook.

Lets take example of axios. It's not related to React, and is generally it's own file in an app.
So axios intereceptors (out of tree code) cannot forcibly navigate the app (on some auth invalid state).

Solution - instance services

Create some state in the app (in Node, instead of React), and make sure it's updated continually by the tree.
Simply said, just create an object with get and set in a .js and export it.
The App.js uses the set here to update the state. And now, get can be used by anywhere (tree or not).

Fortunately for ReactNavigation, it provides a way to create such a service. See https://reactnavigation.org/docs/navigating-without-navigation-prop#usage

Code:

import { createNavigationContainerRef } from "@react-navigation/native";

let navigator = createNavigationContainerRef(); // module state

/**
 * Used by UI root (App.js), not for feature use.
 */
export function setNavigator(navigatorRef) {
  navigator = navigatorRef;
}

/**
 * `window.location` replacement for mobile. Call from anywhere, including outside components.
 *
 * Used by axios, util functions. Usable for feature use.
 * note: don't use inside components. Use proper ReactNavigator functions, hooks.
 *
 * @link https://reactnavigation.org/docs/navigating-without-navigation-prop#usage
 */
export function getNavigator() {
  if (!navigator.isReady()) {
    console.warn("navigator not ready for use!");
    return null;
  }

  return navigator;
}

Note

Such services should be used in out-of-tree code only. It's more in line with React's philosophy (stick to hooks and other constructs inside components).

think: state in the runtime (Browser engine), instead of React.