planttheidea/moize

Issue with React native's bundler (metro) and moize 6.0.0?

Closed this issue ยท 8 comments

Hi,

I'm currently using moize 5.4.7 successfully in a react native project. While upgrading to moize 6.0.0, I get a bundling error in Metro:

[Tue Mar 02 2021 16:46:34.529]  ERROR    Invariant Violation: Module AppRegistry is not a registered callable module (calling runApplication)
[Tue Mar 02 2021 16:46:34.529]  ERROR    Invariant Violation: Module AppRegistry is not a registered callable module (calling runApplication)

It's a generic error, but I'm sure that it's because of the moize 6.0.0 upgrade, since before upgrading it works, and doesn't after the upgrade (even with clearing various React native/npm caches).

Any idea as to how moize 6.0.0 can cause this?

Thanks!

I mean this error is quite opaque, and gives no context on how moize is actually being used. Can you provide some examples?

This seems like maybe a different issue we're having - but maybe not. Unfortunately the errors we're getting in console are pretty useless.


TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context.
* C:\projects\myProject\node_modules\react-native\Libraries\LogBox\LogBox.js:148:8 in registerError
* C:\projects\myProject\node_modules\react-native\Libraries\LogBox\LogBox.js:59:8 in errorImpl
* C:\projects\myProject\node_modules\react-native\Libraries\LogBox\LogBox.js:33:4 in console.error
* C:\projects\myProject\node_modules\expo\build\environment\react-native-logs.fx.js:27:4 in error
* C:\projects\myProject\node_modules\react-native\Libraries\Core\ExceptionsManager.js:104:6 in reportException
* C:\projects\myProject\node_modules\react-native\Libraries\Core\ExceptionsManager.js:171:19 in handleException
* C:\projects\myProject\node_modules\react-native\Libraries\Core\setUpErrorHandling.js:24:6 in handleError
* C:\projects\myProject\node_modules\expo-error-recovery\build\ErrorRecovery.fx.js:9:32 in ErrorUtils.setGlobalHandler$
* C:\projects\myProject\node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
* C:\projects\myProject\node_modules\regenerator-runtime\runtime.js:293:29 in invoke
* C:\projects\myProject\node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
* C:\projects\myProject\node_modules\regenerator-runtime\runtime.js:154:27 in invoke
* C:\projects\myProject\node_modules\regenerator-runtime\runtime.js:164:18 in PromiseImpl.resolve.then$argument_0
* C:\projects\myProject\node_modules\react-native\node_modules\promise\setimmediate\core.js:37:13 in tryCallOne
* C:\projects\myProject\node_modules\react-native\node_modules\promise\setimmediate\core.js:123:24 in setImmediate$argu
* C:\projects\myProject\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:130:14 in _callTimer
* C:\projects\myProject\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:181:14 in _callImmediatesPass
* C:\projects\myProject\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:441:30 in callImmediates
* C:\projects\myProject\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:387:6 in __callImmediates
* C:\projects\myProject\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:135:6 in __guard$argument_0
* C:\projects\myProject\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:364:10 in __guard
* C:\projects\myProject\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:134:4 in flushedQueue
* [native code]:null in flushedQueue

The error we get on the actual device differs a bit though thankfully and I was able to find that the line that it fails on is like this in our code:

const createMemoization = moize({
  maxSize: 1,
  maxArgs: 2,
});

specifically it fails at 756:18 of moize.js - addInstanceProperties >> called from 771:24 of moize.js - createMoizeInstance

The issue has only affected us with React Native so far.

Interesting! So I'm assuming that createMemoization is only used on standard functions, not React components, correct? If you were using this exclusively on React components then I recommend simply using moize.react (which limits size to 1) or the equivalent moize({ isReact: true }). In the old days this kept an infinite cache and was not component-specific, but with v6 it is both maxSize: 1 as default and component instance-specific (more akin to React.memo).

Now, assuming this is for standard functions, I'd be curious to see some examples of how this is actually applied to a function. In the meantime I can create a test for this specific usage (memoized factory based on multiple options) to see if I can spot a specific bug.

Hey @planttheidea thanks for your speedy reply!

No React components used here and actually, conveniently, we only use this function in exactly one way so I can share the full use case - although I think that it's crashing before createMemoization can ever be used.

But in case it helps:

const createMemoization = moize({
  maxSize: 1,
  maxArgs: 2,
});

const filterNotes = createMemoization(
  (excludeUUID, cacheLength, cached) =>
    cached
      .filter(
        n=> n.uuid !== excludeUUID
      )
      .map(({ uuid, type }) => ({
        uuid,
        type,
      }))
);

and the purpose here is to limit the memoization to the first two arguments only, allowing us to change the third argument without triggering a recompute.

I think I have discovered the source of this bug, and boy what a noodler. I could not reproduce this in any environment other than React Native, but lucky I found Snack and created a web-based repro. It gave me some insight to the source of the issue, which was merely that Array.forEach was used. That is only used in one place in moize, and while surprising I can see how it is possible to get that error there. It also aligns with trying to read specific properties from a function object, which is what triggers that specific error.

I'll try to get a candidate fix up shortly, see if it fixes your issue.

Alright I have just published version 6.0.1, which contains what should be a fix for your issue. I decided to just publish the fix rather than give you a beta to test because I feel pretty confident about it, and the change is correct even outside of this fix. That said, I was not able to confirm it does indeed fix the issue with the aforementioned Snack as it was not seeing the new version immediately (they probably leverage a cached version of npm), so if for some reason this does not fix the issue let me know and I'll continue digging.

Array.forEach was used

@planttheidea wow that's such a crazy cause - but it does seem to have been the cause! I just tested moize@6.0.1 and now React Native starts up fine. Thank you so much for the hard work you put into maintaining your multiple libraries!

@bartzy are you able to test whether this fixes your issue? Your symptoms were different but I'm guessing the issue was the same.

It's been a month since this issue surfaced with no mention, and others that had the problem consider it solved, so I'm going to close. Thanks for reporting!