Reactotron: An object with `writable: false` prop in a state breaks SagaMonitor

Created on 7 Mar 2017  路  22Comments  路  Source: infinitered/reactotron

We've recently added reactotron to our project and it's pretty cool.
We've noticed though that it raises an unhandled exception on read-only object' props in sagas and actions attributes.

In this line https://github.com/infinitered/reactotron/blob/f49760fef66a24bd8ab26dd06c570176f0619c0f/packages/reactotron-core-client/src/scrub.js#L46

object[key] = getFunctionName(value);

an error is raised Cannot assign to read only property 'replace' of object. key === 'replace'.

I've noticed that it happens only when an object's key is read-only.

Object.getOwnPropertyDescriptor(object, key)
// result:
{
  configurable: false,
  enumerable: true,
  value: '',
  writable: false
}

Why do we have a read-only value in an object is a good question and we definitely need to change that, but as a quicker solution we would love to have reactotron not break our app [:

I can add a fork where we run

object[key] = getFunctionName(value);

only when

Object.getOwnPropertyDescriptor(object, key).writable

is true. Will that solve the issue?

Most helpful comment

image

Ugh... with a comment like that, I can't even claim it was an accident either.

All 22 comments

Agreed... Reactotron needs to take the hippocratic oath.

Hey, so can you turn off that scrubbing? In your ReactotronConfig.js, add this in:

Reactotron.configure({ safeRecursion: false })

This turns off that scrubbing behaviour.

If you're using react-native-router-flux, logging react components with console.tron.log(), or other things with circular references, you'll have some issues.

Can you give that a shot?

I can also see that replaceValuesWithSomethingWeCanSeeOnTheOtherSideOfSerialization failing on other cases, I really think we ought to protect that logic with try/catch and provide safe error handling

@skellock turning safe-recursion off caused the stack to blow inside an action:

error RangeError: Maximum call stack size exceeded
    at _hasBinary (eval at <anonymous> (apps.js:2071), <anonymous>:29:37)
    at _hasBinary (eval at <anonymous> (apps.js:2071), <anonymous>:38:15)
    at _hasBinary (eval at <anonymous> (apps.js:2071), <anonymous>:49:63)
    at _hasBinary (eval at <anonymous> (apps.js:2071), <anonymous>:49:63)
    at _hasBinary (eval at <anonymous> (apps.js:2071), <anonymous>:49:63)
    at _hasBinary (eval at <anonymous> (apps.js:2071), <anonymous>:38:15)
    at _hasBinary (eval at <anonymous> (apps.js:2071), <anonymous>:49:63)
    at _hasBinary (eval at <anonymous> (apps.js:2071), <anonymous>:49:63)
    at _hasBinary (eval at <anonymous> (apps.js:2071), <anonymous>:49:63)
    at _hasBinary (eval at <anonymous> (apps.js:2071), <anonymous>:38:15)

PS: @cbrwizard and I work on the same codebase

I agree 100%. Not only that, the function is incredibly slow. I have a client with a 6mb JSON object, and it noticably slows down everything.

Is the action logging something like a react component? Or perhaps react-native-router-flux?

That stack overflow is something in socket.io (my nemesis). I can't rely on it to serialize the messages, so that scrub functionality exists to "prep" it for transport.

I've been searching for a better way to serialize that message over the wire. Everything I find and plug in has issues.

@skellock
image

we'd be completely fine with losing some detail on the reactotron logging if the tradeoff is not blowing up our app; do you think there's a quick patch for this?

Gotcha. Somewhere in _events, there's a reference to itself or another object with a circular reference.

One option:

If it's just INIT_DATA/SET causing grief, you can turn that off in a filter in your ReactotronConfig.js in the redux plugin using except.

Another thing we could do is provide a pre-serialize hook inside that same plugin. onBeforeSerialize where you had an opportunity filter out sensitive (or in your case, broken) stuff.

thanks @skellock I'll try that.

about the serializer, do we absolutely need to mutate the original?, how about creating a new object copy, if that's too slow, maybe just do that when mutating fails?

Definitely should not be mutating the original. That's a bug for sure. I'll fix that up.

image

Ugh... with a comment like that, I can't even claim it was an accident either.

@skellock I wasn't gonna say anything about that comment ;D

Here's where i went off the rails. Clone is shallow. Blargh.

@skellock yeah, clone is a bit weird since it does make a deep copy of everything but functions; Functions are referenced directly on the new object.

I assume this is because Ramda intends on a cloned object to be deepEqual to the original

Gotcha. Thx.

What i need is something that recursively walks a structure. Each visit calling an optional callback allowing me to override the value placed inside.

Like this but for JSON and a little more open to callbacks. Preferably a dependency-free package that I didn't write either. =)

@skellock recursive-iterator might get the job done.

or one off this list should be able to replace clone from ramda, deep-copy looks fast and simple

Sweet @burabure . Thx for the refs!

@skellock we ended up disabling reactotron for now ;___; sadly after applying the quick fixes (disable recursion and ignore recursive action stuff) it silently crashed some of our sagas/actions. I hope a fix for this goes up soon because we loved debugging with reactotron

Thx for letting me know.

Reactotron is back at the top of my list of priorities now that I've finished gluegun and wrapping up ignite 2.

Keep ya posted.

Fixed in 1.9. Shipping tomorrow.

@skellock it's working perfectly now! thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Kida007 picture Kida007  路  4Comments

Eyesonly88 picture Eyesonly88  路  4Comments

lndgalante picture lndgalante  路  4Comments

nonameolsson picture nonameolsson  路  5Comments

sylar picture sylar  路  4Comments