ReactJS mobx - 不更新可观察数组

ReactJS mobx - does not update observable array

如果您的商店中有 observable 个列表:

@obserable public Claimslist: IClaim[] = [];

其中 IClaim 是一个接口,claimsIClaim 个对象的数组

IClaim 在其对象中包含一个名为 moveOutDate 的变量 每个声明对象都有 id 个变量。

现在您想通过 claim id

更新或获取该变量

所以你有两个函数:

获取

  /**
   * Gets the moveOutDate of a claim by claim ID
   */
  @action getMoveOutDateById = (id: string) => {
    // Get the claim by id
    const claim = this.Claimslist.find((element: IClaim) => element.id === sessionStorage.getItem('claim-id'));
    if (claim) {
      if (!claim.moveOutDate) {
        return undefined;
      }
      return moment(new Date(claim.moveOutDate)).toDate();
    }

    return undefined;
  }

设置

  /**
   * Sets moveOutDate in claim by claim ID
   */
  @action public setMoveOutDate = (id: string, date: Date) => {
    const claim = this.Claimslist.find((element: IClaim) => element.id === id);
    if (claim) {
      claim.moveOutDate = date.toString();
    }
  }

Okey,但是现在当您首先在 render 中使用 getMoveOutDateById 时,它会工作并呈现日期,但是一旦您调用日期的 setMoveOutDate onUpdate组件,渲染不会更新,但商店会更新,只有刷新页面才能看到更改。

现在我设法解决了这个问题,方法是:

this.Claimslist = this.Claimslist.slice();

并添加 Claimslist 的隐藏输入:

const {Claimslist} = this.props.claimsStore;

<input type="hidden" value={Claimslist} />

如果整个对象是 obserable 并用 @action 包裹,为什么会发生这种情况,为什么不使用 slice() 和隐藏输入它就不会改变?

Mobx observable 只观察浅表值。意思是说我有 @observable claims[] 然后 claims[0] = something 将触发更新但 claims[0].foo = bar 不会 (docs)。要解决此问题,您需要通过在 claim 对象上添加 @observable foo 来使 foo 也可观察。

考虑以下示例。注意 Claim1Claim2 之间的区别。组件完全相同,但 ClaimView2 会更新而 ClaimView1 不会。 Demo

import React from "react";
import { render } from "react-dom";
import { observable, action } from "mobx";
import { observer } from "mobx-react";

class Claim1 {
  moveOutDate: Date;
  constructor() {
    this.moveOutDate = new Date();
  }
}

@observer
class ClaimsView1 extends React.Component {
  @observable claims: Claim1[] = [
    new Claim1(),
    new Claim1()
  ];

  @action.bound
  updateClaims(){
    this.claims.forEach(claim => {
      claim.moveOutDate = new Date();
    })
  }

  render() {
    return <div>
      <pre>
        {"ClaimsView1 = \n" + JSON.stringify(this.claims, null, "  ")}
      </pre>
      <button onClick={this.updateClaims}> Update </button>
    </div>
  }
}


class Claim2 {
  @observable moveOutDate: Date;
  constructor() {
    this.moveOutDate = new Date();
  }
}

@observer
class ClaimsView2 extends React.Component {
  @observable claims: Claim2[] = [
    new Claim2(),
    new Claim2()
  ];

  @action.bound
  updateClaims(){
    this.claims.forEach(claim => {
      claim.moveOutDate = new Date();
    })
  }

  render() {
    return <div>
      <pre>
        {"ClaimsView2 = \n" + JSON.stringify(this.claims, null, "  ")}
      </pre>
      <button onClick={this.updateClaims}> Update </button>
    </div>
  }
}

render(
  <>
    <ClaimsView1 />
    <ClaimsView2 />
  </>,
  document.getElementById("root")
);


更新:

这是我在评论中给出的解决方案demo

import React from "react";
import { render } from "react-dom";
import { observable, action } from "mobx";
import { observer } from "mobx-react";

interface IClaim {
  moveOutDate: Date;
}

@observer
class ClaimsView extends React.Component<{claims: IClaim[]}> {
  @observable claims: IClaim[] = this.props.claims.map(claim => observable(claim))

  updateClaims = () => {
    this.claims.forEach(claim => {
      claim.moveOutDate = new Date();
    })
  }

  render() {
    return <div>
      <pre>
        {"claims = \n" + JSON.stringify(this.claims, null, "  ")}
      </pre>
      <button onClick={this.updateClaims}> Update </button>
    </div>
  }
}


render(
  <ClaimsView claims={[
    { moveOutDate: new Date() },
    { moveOutDate: new Date() }
  ]} />,
  document.getElementById("root")
);

不完全一样,少了@observable,如果是:

class Claim1 {
  @observable moveOutDate: Date;
...