VSDekar
VSDekar

Reputation: 1821

mobx-deep-action with Typescript async/await functions and @action decorator

I am struggeling to setup my project correctly to get rid of the mobx error when running in useStrict(true)

Error

mobx.module.js:2238 Uncaught (in promise) Error: [mobx] Invariant failed: Since strict-mode is enabled, changing observed observable values outside actions is not allowed. Please wrap the code in an `action` if this change is intended. Tried to modify: [email protected]
    at invariant (mobx.module.js:2238)
    at fail (mobx.module.js:2233)
    at checkIfStateModificationsAreAllowed (mobx.module.js:2794)
    at ObservableValue.prepareNewValue (mobx.module.js:750)
    at setPropertyValue (mobx.module.js:1605)
    at WikiStore.set [as tags] (mobx.module.js:1575)
    at WikiStore.<anonymous> (WikiStore.tsx:25)
    at step (tslib.es6.js:91)
    at Object.next (tslib.es6.js:72)
    at fulfilled (tslib.es6.js:62)

My setup compiles but the error is still there, so there must be a mistake in my build chain.

I have used the information provided from the GitHub repo here, but its all a bit vague.

.babelrc

{
"passPerPreset": true,
"presets": [
    {
        "plugins": ["transform-regenerator"]
    },
    {
        "plugins": ["mobx-deep-action"]
    },
    {
      "plugins": ["babel-plugin-transform-decorators-legacy"]
    },
    "es2015", "react", "stage-0"
]
}

webpack.config.js

const path = require("path");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const webpack = require("webpack");
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;

const clientBundleOutputDir = "./wwwroot/dist";

module.exports = (env) => {

    const clientConfig = {
        stats: { modules: false },
        entry: { "main-client": "./Client/index.tsx" },
        resolve: { extensions: [".js", ".jsx", ".ts", ".tsx" ] },
        output: {
            filename: "[name].js",
            path: path.join(__dirname, clientBundleOutputDir),
            publicPath: "/dist/"
        },
        module: {
            rules: [
                { test: /\.s?css$/, use: ["css-hot-loader"].concat(ExtractTextPlugin.extract({fallback: "style-loader", use: ["css-loader", "sass-loader"]})) },
                { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
                { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" },
                { test: /\.(png|svg|jpg|gif|ico)$/, use: ["file-loader"] },
                // { test: /\.tsx?$/, include: /Client/, use: "awesome-typescript-loader?silent=true&useBabel=true&useCache=true" }
                { test: /\.tsx?$/, include: /Client/, use: ["babel-loader", "ts-loader"] }
            ]
        },
        plugins: [
            new CheckerPlugin(),
            new ExtractTextPlugin("site.css"),
            new webpack.SourceMapDevToolPlugin({
                filename: "[file].map",
                moduleFilenameTemplate: path.relative(clientBundleOutputDir, '[resourcePath]')
            })
        ]
    };

    return [clientConfig];
};

I have tried the awesome typescript loader and also the ts-loader, babel-loader combo.

And here my store (short version)

import { autorun, observable, action } from "mobx";
import { Wiki } from "../models/Wiki";
import { Tag } from "../models/Tag";
import * as Api from "../api/api";

export class WikiStore {
    @observable public tags: Tag[] = [];

    constructor() {
        this.getTags();
    }

    @action
    public async getTags() {
        this.tags = await Api.getTags();
    }
}

export const wikiStore = new WikiStore();

As soon as i call wikiStore.getTags() from a React component i get the error. Everything just works fine without useStrict(true) (as expected)

Maybe someone has an idea.

Upvotes: 1

Views: 990

Answers (2)

Daniel
Daniel

Reputation: 2053

Please read here: https://mobx.js.org/best/actions.html#async-await

As a result, @action only applies to the code block until the first await

So const tags = await Api.getTags(); must be wrapped again

@action
public async getTags() {
    const tags = await Api.getTags();
    runInAction(() => {
      this.tags = tags;
    })
}

Upvotes: 0

Tholle
Tholle

Reputation: 112887

I think you still need to split up the await and the assignment:

@action
public async getTags() {
  const tags = await Api.getTags();
  this.tags.replace(tags);
}

Upvotes: 0

Related Questions