Redux DevTools 与 Expo 49 beta(React Native 和 Hermes 引擎)

Not*_*112 5 react-native redux expo react-native-hermes

我正在使用Expo 49 Beta (React Native),它引入了 Hermes 的新调试功能。这意味着还集成了redux-devtools的react-native-debugger已被弃用。我尝试独立使用,但没有成功。redux-devtools


到目前为止我尝试过的:

1.@redux-devtools/remote根据https://github.com/reduxjs/redux-devtools/issues/1382进行补丁

2.用于@redux-devtools/cli通过运行来启动本地服务器redux-devtools --port=8000 --open

3.设置商店

import {devToolsEnhancer} from '@redux-devtools/remote';
import {configureStore} from '@reduxjs/toolkit';

const store = configureStore({
    reducer: rootReducer,
    devTools: true,
    enhancers: [
      devToolsEnhancer({
        name: Platform.OS,
        port: 8000,
        secure: false,
        realtime: true,
      }),
    ],
  });
Run Code Online (Sandbox Code Playgroud)

4.运行应用程序

运行应用程序


5.Redux DevTools设置

redux 开发工具设置



结果:

在此输入图像描述

Not*_*112 1

编辑:这已在最新版本中修复@redux-devtools/remote

\n

显然,Hermes 引擎有问题for await。我设法通过修补@redux-devtools/remote/lib/cjs/devTools.js此 GitHub 问题来使其工作:https://github.com/reduxjs/redux-devtools/issues/1382#issuecomment-1615995161

\n

patch-package\xe2\x98\x9d\xef\xb8\x8f 您可以在上面的链接中找到该补丁。由于StackOverflow支持不是```diff```很好,所以我没有在这里发布。

\n

最终结果(在@redux-devtools/remote@0.8.0撰写本文时)应如下所示:

\n
\'use strict\';\n\nvar _interopRequireDefault = require(\'@babel/runtime/helpers/interopRequireDefault\');\nObject.defineProperty(exports, \'__esModule\', {\n  value: true,\n});\nexports.composeWithDevTools = composeWithDevTools;\nexports.default = void 0;\nvar _defineProperty2 = _interopRequireDefault(\n  require(\'@babel/runtime/helpers/defineProperty\'),\n);\nvar _jsan = require(\'jsan\');\nvar _socketclusterClient = _interopRequireDefault(\n  require(\'socketcluster-client\'),\n);\nvar _configureStore = _interopRequireDefault(require(\'./configureStore\'));\nvar _constants = require(\'./constants\');\nvar _rnHostDetect = _interopRequireDefault(require(\'rn-host-detect\'));\nvar _utils = require(\'@redux-devtools/utils\');\nfunction async(fn) {\n  setTimeout(fn, 0);\n}\nfunction str2array(str) {\n  return typeof str === \'string\'\n    ? [str]\n    : str && str.length > 0\n    ? str\n    : undefined;\n}\nfunction getRandomId() {\n  return Math.random().toString(36).substr(2);\n}\nclass DevToolsEnhancer {\n  constructor() {\n    var _this = this;\n    (0, _defineProperty2.default)(this, \'errorCounts\', {});\n    (0, _defineProperty2.default)(this, \'send\', () => {\n      if (!this.instanceId) {\n        this.instanceId = (this.socket && this.socket.id) || getRandomId();\n      }\n      try {\n        fetch(this.sendTo, {\n          method: \'POST\',\n          headers: {\n            \'content-type\': \'application/json\',\n          },\n          body: JSON.stringify({\n            type: \'STATE\',\n            id: this.instanceId,\n            name: this.instanceName,\n            payload: (0, _jsan.stringify)(this.getLiftedState()),\n          }),\n        }).catch(function (err) {\n          console.log(err);\n        });\n      } catch (err) {\n        console.log(err);\n      }\n    });\n    (0, _defineProperty2.default)(this, \'handleMessages\', message => {\n      if (\n        message.type === \'IMPORT\' ||\n        (message.type === \'SYNC\' &&\n          this.socket.id &&\n          message.id !== this.socket.id)\n      ) {\n        this.store.liftedStore.dispatch({\n          type: \'IMPORT_STATE\',\n\n          nextLiftedState: (0, _jsan.parse)(message.state),\n        });\n      } else if (message.type === \'UPDATE\') {\n        this.relay(\'STATE\', this.getLiftedState());\n      } else if (message.type === \'START\') {\n        this.isMonitored = true;\n        if (typeof this.actionCreators === \'function\') {\n          this.actionCreators = this.actionCreators();\n        }\n        this.relay(\'STATE\', this.getLiftedState(), this.actionCreators);\n      } else if (message.type === \'STOP\' || message.type === \'DISCONNECTED\') {\n        this.isMonitored = false;\n        this.relay(\'STOP\');\n      } else if (message.type === \'ACTION\') {\n        this.dispatchRemotely(message.action);\n      } else if (message.type === \'DISPATCH\') {\n        this.store.liftedStore.dispatch(message.action);\n      }\n    });\n    (0, _defineProperty2.default)(this, \'sendError\', errorAction => {\n      // Prevent flooding\n      if (errorAction.message && errorAction.message === this.lastErrorMsg) {\n        return;\n      }\n      this.lastErrorMsg = errorAction.message;\n      async(() => {\n        this.store.dispatch(errorAction);\n        if (!this.started) {\n          this.send();\n        }\n      });\n    });\n    (0, _defineProperty2.default)(this, \'stop\', keepConnected => {\n      this.started = false;\n      this.isMonitored = false;\n      if (!this.socket) {\n        return;\n      }\n      void this.socket.unsubscribe(this.channel);\n      this.socket.closeChannel(this.channel);\n      if (!keepConnected) {\n        this.socket.disconnect();\n      }\n    });\n    (0, _defineProperty2.default)(this, \'start\', () => {\n      if (\n        this.started ||\n        (this.socket && this.socket.getState() === this.socket.CONNECTING)\n      ) {\n        return;\n      }\n      this.socket = _socketclusterClient.default.create(this.socketOptions);\n      void (async () => {\n        let consumer = this.socket.listener(\'error\').createConsumer();\n        while (true) {\n          const {value: data, done} = await consumer.next();\n          if (done) {\n            break;\n          }\n\n          // for await (const data of this.socket.listener(\'error\')) {\n          // if we\'ve already had this error before, increment it\'s counter, otherwise assign it \'1\' since we\'ve had the error once.\n\n          this.errorCounts[data.error.name] = this.errorCounts.hasOwnProperty(\n            data.error.name,\n          )\n            ? this.errorCounts[data.error.name] + 1\n            : 1;\n          if (this.suppressConnectErrors) {\n            if (this.errorCounts[data.error.name] === 1) {\n              console.log(\n                \'remote-redux-devtools: Socket connection errors are being suppressed. \' +\n                  \'\\n\' +\n                  "This can be disabled by setting suppressConnectErrors to \'false\'.",\n              );\n              console.log(data.error);\n            }\n          } else {\n            console.log(data.error);\n          }\n        }\n      })();\n      void (async () => {\n        let consumer = this.socket.listener(\'connect\').createConsumer();\n        while (true) {\n          const {value: data, done} = await consumer.next();\n          if (done) {\n            break;\n          }\n\n          // for await (const data of this.socket.listener(\'connect\')) {\n          console.log(\'connected to remotedev-server\');\n          this.errorCounts = {}; // clear the errorCounts object, so that we\'ll log any new errors in the event of a disconnect\n          this.login();\n        }\n      })();\n      void (async () => {\n        let consumer = this.socket.listener(\'disconnect\').createConsumer();\n        while (true) {\n          const {value: data, done} = await consumer.next();\n          if (done) {\n            break;\n          }\n\n          // for await (const data of this.socket.listener(\'disconnect\')) {\n          this.stop(true);\n        }\n      })();\n    });\n    (0, _defineProperty2.default)(this, \'checkForReducerErrors\', function () {\n      let liftedState =\n        arguments.length > 0 && arguments[0] !== undefined\n          ? arguments[0]\n          : _this.getLiftedStateRaw();\n      if (liftedState.computedStates[liftedState.currentStateIndex].error) {\n        if (_this.started) {\n          _this.relay(\n            \'STATE\',\n            (0, _utils.filterStagedActions)(liftedState, _this.filters),\n          );\n        } else {\n          _this.send();\n        }\n        return true;\n      }\n      return false;\n    });\n    (0, _defineProperty2.default)(this, \'monitorReducer\', function () {\n      let state =\n        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n      let action = arguments.length > 1 ? arguments[1] : undefined;\n      _this.lastAction = action.type;\n      if (\n        !_this.started &&\n        _this.sendOnError === 2 &&\n        _this.store.liftedStore\n      ) {\n        async(_this.checkForReducerErrors);\n      } else if (action.action) {\n        if (\n          _this.startOn &&\n          !_this.started &&\n          _this.startOn.indexOf(action.action.type) !== -1\n        ) {\n          async(_this.start);\n        } else if (\n          _this.stopOn &&\n          _this.started &&\n          _this.stopOn.indexOf(action.action.type) !== -1\n        ) {\n          async(_this.stop);\n        } else if (\n          _this.sendOn &&\n          !_this.started &&\n          _this.sendOn.indexOf(action.action.type) !== -1\n        ) {\n          async(_this.send);\n        }\n      }\n      return state;\n    });\n    (0, _defineProperty2.default)(this, \'enhance\', function () {\n      let options =\n        arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n      _this.init({\n        ...options,\n        hostname: (0, _rnHostDetect.default)(options.hostname || \'localhost\'),\n      });\n      const realtime =\n        typeof options.realtime === \'undefined\'\n          ? process.env.NODE_ENV === \'development\'\n          : options.realtime;\n      if (!realtime && !(_this.startOn || _this.sendOn || _this.sendOnError)) {\n        return f => f;\n      }\n      const maxAge = options.maxAge || 30;\n      return next => {\n        return (reducer, initialState) => {\n          _this.store = (0, _configureStore.default)(\n            next,\n            _this.monitorReducer,\n            {\n              maxAge,\n              trace: options.trace,\n              traceLimit: options.traceLimit,\n              shouldCatchErrors: !!_this.sendOnError,\n              shouldHotReload: options.shouldHotReload,\n              shouldRecordChanges: options.shouldRecordChanges,\n              shouldStartLocked: options.shouldStartLocked,\n              pauseActionType: options.pauseActionType || \'@@Paused\',\n            },\n          )(reducer, initialState);\n          if (realtime) {\n            _this.start();\n          }\n          _this.store.subscribe(() => {\n            if (_this.isMonitored) {\n              _this.handleChange(\n                _this.store.getState(),\n                _this.getLiftedStateRaw(),\n                maxAge,\n              );\n            }\n          });\n          return _this.store;\n        };\n      };\n    });\n  }\n  getLiftedStateRaw() {\n    return this.store.liftedStore.getState();\n  }\n  getLiftedState() {\n    return (0, _utils.filterStagedActions)(\n      this.getLiftedStateRaw(),\n      this.filters,\n    );\n  }\n  relay(type, state, action, nextActionId) {\n    const message = {\n      type,\n\n      id: this.socket.id,\n      name: this.instanceName,\n      instanceId: this.appInstanceId,\n    };\n    if (state) {\n      message.payload =\n        type === \'ERROR\'\n          ? state\n          : (0, _jsan.stringify)(\n              (0, _utils.filterState)(\n                state,\n                type,\n                this.filters,\n                this.stateSanitizer,\n                this.actionSanitizer,\n                nextActionId,\n              ),\n            );\n    }\n    if (type === \'ACTION\') {\n      message.action = (0, _jsan.stringify)(\n        !this.actionSanitizer\n          ? action\n          : this.actionSanitizer(action.action, nextActionId - 1),\n      );\n      message.isExcess = this.isExcess;\n      message.nextActionId = nextActionId;\n    } else if (action) {\n      message.action = action;\n    }\n\n    void this.socket.transmit(this.socket.id ? \'log\' : \'log-noid\', message);\n  }\n  dispatchRemotely(action) {\n    try {\n      const result = (0, _utils.evalAction)(action, this.actionCreators);\n      this.store.dispatch(result);\n    } catch (e) {\n      this.relay(\'ERROR\', e.message);\n    }\n  }\n  init(options) {\n    this.instanceName = options.name;\n    this.appInstanceId = getRandomId();\n    const {blacklist, whitelist, denylist, allowlist} = options.filters || {};\n    this.filters = (0, _utils.getLocalFilter)({\n      actionsDenylist:\n        denylist ??\n        options.actionsDenylist ??\n        blacklist ??\n        options.actionsBlacklist,\n      actionsAllowlist:\n        allowlist ??\n        options.actionsAllowlist ??\n        whitelist ??\n        options.actionsWhitelist,\n    });\n    if (options.port) {\n      this.socketOptions = {\n        port: options.port,\n        hostname: options.hostname || \'localhost\',\n        secure: options.secure,\n      };\n    } else {\n      this.socketOptions = _constants.defaultSocketOptions;\n    }\n    this.suppressConnectErrors =\n      options.suppressConnectErrors !== undefined\n        ? options.suppressConnectErrors\n        : true;\n    this.startOn = str2array(options.startOn);\n    this.stopOn = str2array(options.stopOn);\n    this.sendOn = str2array(options.sendOn);\n    this.sendOnError = options.sendOnError;\n    if (this.sendOn || this.sendOnError) {\n      this.sendTo =\n        options.sendTo ||\n        `${this.socketOptions.secure ? \'https\' : \'http\'}://${\n          this.socketOptions.hostname\n        }:${this.socketOptions.port}`;\n      this.instanceId = options.id;\n    }\n    if (this.sendOnError === 1) {\n      (0, _utils.catchErrors)(this.sendError);\n    }\n    if (options.actionCreators) {\n      this.actionCreators = () =>\n        (0, _utils.getActionsArray)(options.actionCreators);\n    }\n    this.stateSanitizer = options.stateSanitizer;\n    this.actionSanitizer = options.actionSanitizer;\n  }\n  login() {\n    void (async () => {\n      try {\n        const channelName = await this.socket.invoke(\'login\', \'master\');\n        this.channel = channelName;\n        let consumer = this.socket.subscribe(channelName).createConsumer();\n        while (true) {\n          const {value: data, done} = await consumer.next();\n          if (done) {\n            break;\n          }\n\n          // for await (const data of this.socket.subscribe(channelName)) {\n          this.handleMessages(data);\n        }\n      } catch (error) {\n        console.log(error);\n      }\n    })();\n    this.started = true;\n    this.relay(\'START\');\n  }\n\n  handleChange(state, liftedState, maxAge) {\n    if (this.checkForReducerErrors(liftedState)) {\n      return;\n    }\n    if (this.lastAction === \'PERFORM_ACTION\') {\n      const nextActionId = liftedState.nextActionId;\n      const liftedAction = liftedState.actionsById[nextActionId - 1];\n      if ((0, _utils.isFiltered)(liftedAction.action, this.filters)) {\n        return;\n      }\n      this.relay(\'ACTION\', state, liftedAction, nextActionId);\n      if (!this.isExcess && maxAge) {\n        this.isExcess = liftedState.stagedActionIds.length >= maxAge;\n      }\n    } else {\n      if (this.lastAction === \'JUMP_TO_STATE\') {\n        return;\n      }\n      if (this.lastAction === \'PAUSE_RECORDING\') {\n        this.paused = liftedState.isPaused;\n      } else if (this.lastAction === \'LOCK_CHANGES\') {\n        this.locked = liftedState.isLocked;\n      }\n      if (this.paused || this.locked) {\n        if (this.lastAction) {\n          this.lastAction = undefined;\n        } else {\n          return;\n        }\n      }\n      this.relay(\n        \'STATE\',\n        (0, _utils.filterStagedActions)(liftedState, this.filters),\n      );\n    }\n  }\n}\nvar _default = options => new DevToolsEnhancer().enhance(options);\nexports.default = _default;\nconst compose = options =>\n  function () {\n    for (\n      var _len = arguments.length, funcs = new Array(_len), _key = 0;\n      _key < _len;\n      _key++\n    ) {\n      funcs[_key] = arguments[_key];\n    }\n    return function () {\n      const devToolsEnhancer = new DevToolsEnhancer();\n      function preEnhancer(createStore) {\n        return (reducer, preloadedState) => {\n          devToolsEnhancer.store = createStore(reducer, preloadedState);\n          return {\n            ...devToolsEnhancer.store,\n            dispatch: action =>\n              devToolsEnhancer.locked\n                ? action\n                : devToolsEnhancer.store.dispatch(action),\n          };\n        };\n      }\n      for (\n        var _len2 = arguments.length, args = new Array(_len2), _key2 = 0;\n        _key2 < _len2;\n        _key2++\n      ) {\n        args[_key2] = arguments[_key2];\n      }\n      return [preEnhancer, ...funcs].reduceRight(\n        (composed, f) => f(composed),\n        devToolsEnhancer.enhance(options)(...args),\n      );\n    };\n  };\nfunction composeWithDevTools() {\n  for (\n    var _len3 = arguments.length, funcs = new Array(_len3), _key3 = 0;\n    _key3 < _len3;\n    _key3++\n  ) {\n    funcs[_key3] = arguments[_key3];\n  }\n  if (funcs.length === 0) {\n    return new DevToolsEnhancer().enhance();\n  }\n  if (funcs.length === 1 && typeof funcs[0] === \'object\') {\n    return compose(funcs[0]);\n  }\n  return compose({})(...funcs);\n}\n
Run Code Online (Sandbox Code Playgroud)\n