now() returns not a relevant time when no active subscriptions

Open
#330 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
35/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Stale
Tech stack
typescript
Domain
frontend

Research direction

Start in src/now.ts at the _isComputingDerivation check around lines 35-38, then run the TypeScript reproduction from the issue and observe behavior before and after disposer() removes the reaction. The fix is done when imperativeCheck continues reporting decreasing remaining times after the reaction is removed and stops when the counter reaches zero.

Written by the indexing model from the issue text.

Description

There is a fix in #40, but it seems broken now, code:

export function now(interval: number | "frame" = 1000) {
    if (!_isComputingDerivation()) {
        // See #40
        return Date.now()
    }
 ...

In case no subscriptions, _isComputingDerivation always return false. I am not sure why, even if wrap now() in action.

Reproduction:

import {makeAutoObservable, reaction} from "mobx";
import {now} from "mobx-utils";

class Counter {
  stopTime: number = 0;

  get remaining() {
    const delta = this.stopTime - now(1000);
    return delta > 0 ? delta : 0;
  }

  get done() {
    return this.remaining <= 0;
  }

  constructor() {
    makeAutoObservable(this);
  }

  start(duration: number) {
    this.stopTime = Date.now() + duration;
  }

  imperativeCheck() {
    console.log(`imperativeCheck: remaining=${this.remaining} done=${this.done}`);
  }
}

const counter = new Counter();
const disposer = reaction(
  () => counter.remaining,
  (remaining) => {
    console.log(`reactiveCheck: remaining=${remaining}`);
  },
);

console.log("start timer for 10s");
counter.start(10 * 1000);
let imperativeCheckCounts = 0;
scheduleImperativeCheck();

function scheduleImperativeCheck() {
  imperativeCheckCounts++;
  setTimeout(() => {
    counter.imperativeCheck();
    if (imperativeCheckCounts === 2) {
      console.log("Now we remove reaction");
      disposer();
    }
    if (!counter.done) scheduleImperativeCheck();
  }, 2000);
}

Result:

start timer for 10s
reactiveCheck: remaining=10000
reactiveCheck: remaining=8998
imperativeCheck: remaining=8998 done=false
reactiveCheck: remaining=7994
reactiveCheck: remaining=6993
imperativeCheck: remaining=6993 done=false
Now we remove reaction
Called `get` of a subscribingObservable outside a reaction. Current value will be returned but no new subscription has started
Called `get` of a subscribingObservable outside a reaction. Current value will be returned but no new subscription has started
imperativeCheck: remaining=6993 done=false
Called `get` of a subscribingObservable outside a reaction. Current value will be returned but no new subscription has started
Called `get` of a subscribingObservable outside a reaction. Current value will be returned but no new subscription has started
imperativeCheck: remaining=6993 done=false
Called `get` of a subscribingObservable outside a reaction. Current value will be returned but no new subscription has started
Called `get` of a subscribingObservable outside a reaction. Current value will be returned but no new subscription has started
imperativeCheck: remaining=6993 done=false
Called `get` of a subscribingObservable outside a reaction. Current value will be returned but no new subscription has started
Called `get` of a subscribingObservable outside a reaction. Current value will be returned but no new subscription has started
imperativeCheck: remaining=6993 done=false
Called `get` of a subscribingObservable outside a reaction. Current value will be returned but no new subscription has started

Expected behaviour: After removing reaction, imperativeCheck should log decreasing times and stop...

Note: this is a somewhat artifical example. In the actual application we have some logic based on the current time, which is used in many places, some of which are in the reactive context, whereas others are not. In theory, we might write this logic twice: one with now() from mobx-utils, and the other with Date.now(). However, it is a huge duplication of code and makes the code unreadable.

Dominant language
TypeScript
Stars
1.2k
Forks
130
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from mobxjs/mobx-utils

All issues in mobxjs/mobx-utils

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.