mobxjs / mobx-state-tree

Full-featured reactive state management without the boilerplate
https://mobx-state-tree.js.org/
MIT License
6.9k stars 640 forks source link

getMembers does not work as expected, behaviour change between 5.1.0 and 5.4.1 #2173

Closed cemcakirlar closed 2 months ago

cemcakirlar commented 2 months ago

Bug report

Sandbox link or minimal reproduction code

https://codesandbox.io/p/sandbox/mobx-state-tree-todolist-forked-gn69gv?file=%2Fmodels%2FTodo.js

Describe the expected behavior the code should display keys of props, volatiles, views and actions

{
  "_keys_properties": [
    "todos",
    "id",
    "title",
    "finished",
    "nameProp",
    "surnameProp"
  ],
  "_keys_volatile": [
    "newTodoTitle",
    "someVolatile",
    "someOtherVolatile"
  ],
  "_keys_views": [
    "$treenode",
    "toJSON",
    "unfinishedTodoCount",
    "addTodo",
    "handleChange",
    "handleNewTodoClick",
    "remainingTasks",
    "toggle",
    "view",
    "___debugMembers"
  ],
  "_keys_actions": [
    "addTodo",
    "handleChange",
    "handleNewTodoClick",
    "toggle"
  ]
}

A screenshot from my development environment for the same code but different more rich model image

Describe the observed behavior

the code cannot display keys of props, volatiles, views and actions correctly

it mixes actions into views, views into volatiles (not existing in the sample but see attached image, *_calculated props are views but shown in volatiles)

my initial inspection shows it was working fine with version 5.1.0, some code I copied from an old project did not give correct results, I am on 5.4.1

this may be related to types.compose(), a straight simple types.model() seems to be working fine with getMembers(), needs more inspection though.

coolsoftwaretyler commented 2 months ago

Thanks for filing this @cemcakirlar, and sorry for the inconvenience!

For you or anyone else interested in figuring this out, here's a comparison of all the changes between the latest preview release and v5.1.0: https://github.com/mobxjs/mobx-state-tree/compare/v5.1.0...v6.0.0-pre.2

This should be a straightforward bug to resolve, I think the process would look like:

  1. Write a test case that exercises getMembers and correctly categorizes members - we should be able to use the reproduction that @cemcakirlar made to figure that out
  2. Verify it works as expected on v.5.1.0
  3. Verify it fails on latest, v6.0.0-pre.2
  4. git bisect between those commits, running the test along the way and identify the problem commit
  5. See if we can safely roll back, modify, or fix the commit that broke this existing behavior.

I'll label this as a bug, and I think it's somewhere between easy -> intermediate. Requires a little comfortability with git and the codebase overall, but the scope of the change seems pretty limited, so this would be a good second or third contribution for anyone out there who's looking to help out.

cemcakirlar commented 2 months ago

Thank you @coolsoftwaretyler for categorizing this issue. I am a "test/debug monster" which I sometimes not want to be. getMembers etc. are nice utils for dynamically inspecting the models at runtime, my use case was that. That use case is not a showstopper for me but nice to have and others I think. Cheers.

evinosheaforward commented 2 months ago

I started to take a look at this using the approach @coolsoftwaretyler outlined (git bisect) I got the sandbox example from @cemcakirlar running in a test.

I'm now trying to get the same example to run vs 5.1.0 so we can compare the differences in the properties, volatiles, views, and actions.

Unfortunately, checking out v5.1.0 and running the tests has proven tricky. (Note for anyone else, you need lerna@4.0) The main issue is the exiting core/reflection.tests.ts fails with the following:

$ yarn run jest --testPathPattern=/__tests__/core/reflection.test.ts
yarn run v1.22.22
$ /home/eoshea/mobx-state-tree/node_modules/.bin/jest --testPathPattern=/__tests__/core/reflection.test.ts
 FAIL  packages/mobx-state-tree/__tests__/core/reflection.test.ts
  ● Test suite failed to run

    TypeError: Cannot read properties of undefined (reading 'some')

      at exports.install (node_modules/whatwg-url/dist/URL.js:84:20)
      at exports.installInterfaces (node_modules/jsdom/lib/jsdom/living/interfaces.js:202:24)
      at setupWindow (node_modules/jsdom/lib/jsdom/browser/Window.js:63:3)
      at new Window (node_modules/jsdom/lib/jsdom/browser/Window.js:107:3)
      at exports.createWindow (node_modules/jsdom/lib/jsdom/browser/Window.js:38:10)
      at new JSDOM (node_modules/jsdom/lib/api.js:36:20)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.489 s
Ran all test suites matching /\/__tests__\/core\/reflection.test.ts/i.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

I will update as I make progress.

coolsoftwaretyler commented 2 months ago

Thanks @evinosheaforward! I totally forgot about the old Lerna setup. Definitely a little less straightforward than I initially expected. Let me know if you wanna talk about a smoother approach.

It's possible we could identify a likely culprit without git bisect, just by reading the history, and work from the current main branch which should be a lot easier

evinosheaforward commented 2 months ago

Okay so I got the tests running on v5.1.0. Just had to get my environment setup correctly (not as easy as it was with bun!)

For brute force exploring the differences, I took the existing v5.1.0 reflection tests and ran them vs both 5.1.0 and master and compared the views, properties, volatiles, and actions. This did yield a result of extra views on master (extra view for each action), so I started a git bisect looking for the commit where that changed and found 62e7e8bafe8bf07f4694e429f734bb12323fd8ef as the "bad commit".

I will look into writing an explicit test of the differences so I can be more clear about what I found. Especially so we can understand if this is the root of this issue.

evinosheaforward commented 2 months ago

Okay, updated the tests off of master to be failing to demonstrate the change https://github.com/evinosheaforward/mobx-state-tree/commit/2d0b4596a0d66ca425e141fd09b25ad4266007de

this looks like this is the bug with actions mixing into views

@coolsoftwaretyler PR with test and fix: https://github.com/mobxjs/mobx-state-tree/pull/2174

edit: this is only for actions -> views

evinosheaforward commented 2 months ago

For the views -> volatiles, I can't tell if this is a bug or not, given this line: https://github.com/mobxjs/mobx-state-tree/blob/master/src/core/mst-operations.ts#L890

      if (isComputedProp(target, key)) reflected.views.push(key)

but, unless I am mistaken, the commit related to flow actions is the only change since v5.1.0.

So it seems like the *_calculated props should be considered views, but also this behavior was not changed since v5.1.0.

@cemcakirlar if you have a reproduction of the views -> volatiles issue I could try to understand the issue better

coolsoftwaretyler commented 2 months ago

Hey @evinosheaforward and @cemcakirlar - I can't reproduce the views -> volatiles issue.

On this PR branch, I tried running this test:

// Append to __tests__/core/reflection.test.ts
test("reflection - snapshot test with all options", () => {
  const TestModelDemo = types
    .model("TestModelDemo", {
      name: types.string
    })
    .actions((self) => {
      return {
        actionName() {
          return 1
        }
      }
    })
    .volatile((self) => {
      return {
        volatileProperty: { propName: "halo" }
      }
    })
    .actions((self) => {
      return {
        flowAction: flow(function* flowAction() {
          const promise = new Promise((resolve) => {
            resolve(true)
          })
          yield promise
        })
      }
    })
    .views((self) => {
      return {
        get viewName() {
          return 1
        },
        notComputed() {
          return 1
        }
      }
    })

  const testAllOfEm = TestModelDemo.create({ name: "John" })
  const reflection = getMembers(testAllOfEm)

  expect(reflection).toMatchObject({
    actions: ["actionName", "flowAction"],
    flowActions: ["flowAction"],
    volatile: ["volatileProperty"],
    views: ["$treenode", "toJSON", "viewName", "notComputed"]
  })
})

And everything seems to be working as expected. Volatile has just the volatileProperty, and neither the computed view, nor the regular function view cross over into the reflected volatile array.

Let me know if you've got a reproduction. Otherwise we may close this out once we merge #2174.

cemcakirlar commented 2 months ago

For the views -> volatiles, I can't tell if this is a bug or not, given this line: https://github.com/mobxjs/mobx-state-tree/blob/master/src/core/mst-operations.ts#L890

      if (isComputedProp(target, key)) reflected.views.push(key)

but, unless I am mistaken, the commit related to flow actions is the only change since v5.1.0.

So it seems like the *_calculated props should be considered views, but also this behavior was not changed since v5.1.0.

@cemcakirlar if you have a reproduction of the views -> volatiles issue I could try to understand the issue better

Unfortunately I also cannot reproduce the views->volatiles issue. I played around with the codesandbox example I created but could not get something like the screenshot I provided. Since the code for the screenshot is several commits away in my project or maybe never committed and discarded, I cannot provide a sample code for views -> volatiles issue. Sorry. Or maybe it is a good thing, we do not have a bug there :)

coolsoftwaretyler commented 2 months ago

Sounds good. I think for now let's close this with https://github.com/mobxjs/mobx-state-tree/pull/2174.

coolsoftwaretyler commented 2 months ago

This fix is available in 5.4.2 now: https://github.com/mobxjs/mobx-state-tree/releases/tag/v5.4.2, will be shipped with v6 as well.