最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - mobx computed function not re-running when observable value is updated in test - Stack Overflow

programmeradmin1浏览0评论

I am attempting to write a unit test for mobx-based reaction. For some reason, when the @observable value is updated in an @action, the @puted function does not re-run as you'd expect.

Code:

STORE

class NameStore {
    @observable name;

    @action setName(name) {
        this.name = name;
    }
}

COMPONENT 1

@observer
class Name {
    @puted get name() {
        if (this.props.nameStore.name) {
            return `${this.props.nameStore.name} is awesome!`;
        }

        return null;
    }

    render() {
        return (
            <div className="name">
                {this.name}
            </div>
        );
    }
}

COMPONENT 2

@observer
class Name {
    setName() {
        this.props.nameStore.setName(this.name);
    }

    render() {
        return (
            <form onSubmit={this.setName.bind(this)}>
                <input type="text" ref={input => this.name = input} />
            </form>
        );
    }
}

TEST

define('Name ponent', () => {
    let markup;

    beforeEach(() => {
        const nameStore = new NameStore();
        markup = mount(
            <div>
                <Component1 nameStore={nameStore} />
                <Component2 nameStore={nameStore} />
            </div>
        );
    });

    it('should re-render name when updated', (done) => {
        expect(markup.find('.name').text()).to.be.blank;

        markup.find('form input').first().value = "john";
        markup.find('form').simulate('submit');

        expect(markup.find('.name').text()).to.equal("john is awesome")
    });
});

For some reason, in the test, the actual value of {this.name} in Component1 remains unchanged even though I'm able to verify that the setName function in the store is being called properly and with the correct value.

Any help as to why Component1 is not re-rendering would be much appreciated.

Also, this is a contrived example as the actual example is proprietary..so forgive me if this example feels dumb :)

Thanks!

I am attempting to write a unit test for mobx-based reaction. For some reason, when the @observable value is updated in an @action, the @puted function does not re-run as you'd expect.

Code:

STORE

class NameStore {
    @observable name;

    @action setName(name) {
        this.name = name;
    }
}

COMPONENT 1

@observer
class Name {
    @puted get name() {
        if (this.props.nameStore.name) {
            return `${this.props.nameStore.name} is awesome!`;
        }

        return null;
    }

    render() {
        return (
            <div className="name">
                {this.name}
            </div>
        );
    }
}

COMPONENT 2

@observer
class Name {
    setName() {
        this.props.nameStore.setName(this.name);
    }

    render() {
        return (
            <form onSubmit={this.setName.bind(this)}>
                <input type="text" ref={input => this.name = input} />
            </form>
        );
    }
}

TEST

define('Name ponent', () => {
    let markup;

    beforeEach(() => {
        const nameStore = new NameStore();
        markup = mount(
            <div>
                <Component1 nameStore={nameStore} />
                <Component2 nameStore={nameStore} />
            </div>
        );
    });

    it('should re-render name when updated', (done) => {
        expect(markup.find('.name').text()).to.be.blank;

        markup.find('form input').first().value = "john";
        markup.find('form').simulate('submit');

        expect(markup.find('.name').text()).to.equal("john is awesome")
    });
});

For some reason, in the test, the actual value of {this.name} in Component1 remains unchanged even though I'm able to verify that the setName function in the store is being called properly and with the correct value.

Any help as to why Component1 is not re-rendering would be much appreciated.

Also, this is a contrived example as the actual example is proprietary..so forgive me if this example feels dumb :)

Thanks!

Share Improve this question asked Feb 19, 2018 at 22:58 MarkMark 11.1k6 gold badges34 silver badges48 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 4

There are few mistakes:

  • Neither of the ponents extends React.Component
  • @puted should be in the store
  • input should bind to onChange and updates its value
  • Browser may plain input value is undefined. It's better to set it to empty string.
  • input value is in event.target.value, ref={input => this.name = input} will assign this.name to the html ponent.
  • The rule of thumb when deal with form is to callevent.preventDefault()

The code below is a pleted working example based on your code:

import React from 'react';
import { observable, action, puted } from 'mobx';
import { observer } from 'mobx-react';

class NameStore {
  @observable name = '';

  @action
  setName = name => {
    this.name = name;
  }

  @puted
  get awesomeName() {
    return this.name ? `${this.name} is awesome!` : '';
  }
}

@observer
class NameField extends React.Component {
  render() {
    const { nameStore } = this.props;
    return <div className="name"> {nameStore.awesomeName} </div>;
  }
}

@observer
class NameInput extends React.Component {
  render() {
    const { nameStore } = this.props;
    return (
      <form>
        <input
          type="text"
          onChange={this.onChange}
          value={nameStore.name}
        />
      </form>
    );
  }

  onChange = e => {
    const { nameStore } = this.props;
    nameStore.setName(e.target.value);
    e.preventDefault();
  }
}

@observer
class App extends React.Component {
  nameStore = new NameStore();

  render() {
    return (
      <div>
        <NameField nameStore={this.nameStore} />
        <NameInput nameStore={this.nameStore} />
      </div>
    );
  }
}

export default App;

Extra note: Since the arrow function has been used, I don't have to call .bind(this).

发布评论

评论列表(0)

  1. 暂无评论