mobx系列(五)-Mobx常见问题及解决方案(2)observable state更新后组件不主动更新问题

在《mobx系列(二)-mobx主要概念》中解释过mobx对什么作出反应,如果对这一部分内容理解不清楚,开发中经常会遇到store中状态更新了,但是组件没有重新渲染的问题。本文简单列举几种情况及解决方法。

1、缓存observables 属性并存储在本地

这是文档中的一个示例,指的是从 observable 属性中提取数据并存储,这样的数据是不会被追踪的:

class User {
  @observable name
}

class Profile extends React.Component {
  name

  componentWillMount() {
    // 错误的
    // 这会间接引用 user.name 并只拷贝值一次!未来的更新不会被追踪,因为生命周期钩子不是响应的
    // 像这样的赋值会创建冗余数据
    this.name = this.props.user.name
  }

  render() {
    return <div>{this.name}</div>
  }
}
这样name的变化不会触发Profile组件的更新。正确的方法通过不将 observables 的值存储在本地(显然,上面的示例很简单,但却是有意为之的):
class User {
  @observable name
}

class Profile extends React.Component {
  render() {
    return <div>{this.props.user.name}</div>
  }
}

这样name的变化不会触发Profile组件的更新。正确的方法通过不将 observables 的值存储在本地(显然,上面的示例很简单,但却是有意为之的)

class User {
  @observable name
}

class Profile extends React.Component {
  render() {
    return <div>{this.props.user.name}</div>
  }
}

或通过将其定义为计算属性:

class User {
  @observable name
}

class Profile extends React.Component {
  @computed get name() {
    // 正确的; 计算属性会追踪 `user.name` 属性
    return this.props.user.name
  }

  render() {
    return <div>{this.name}</div>
  }
}

 2、将一个object传递给子组件:

有如下observable状态:

@observable testObj=[{id: '1', value: '1'}];

在组件中的使用为:

import React, {Component} from 'react';
import {Table } from 'antd';
import {inject, observer} from 'mobx-react';
@inject('configureStore')
@observer
export default class EditableTable extends Component {
  constructor(props) {
    super(props);
  }
getColumns(){
// return columns;
}
  render() {
const {testObj} = this.props.configureStore;
    const columns = this.getColumns(dataSource);
    return (
        <Table
          dataSource={testObj}
          columns={columns}
        /> );
  }
}

EditableTable组件没有观察testObj 中的属性值,直接将testObj传递给Table组件,Table组件并不会因为testObj中属性值的改变而re-render,此时的解决方法为:

a、让子组件作为观察者,在子组件的render方法中通过

const {testObj} = this.props.configureStore;

获取并读取testObj的可观察属性,以此达到子组件可根据observable state值的改变而re-render的效果。

当然在上例中该方法并不适用,因为antd的Table组件不会成为观察者。

b、在上例中读取testObj后,父组件直接读取其中的可观察状态,比如:

const dataSource = testObj.map(item => ({...item}));

上例可修改为:

import React, {Component} from 'react';
import {Table } from 'antd';
import {inject, observer} from 'mobx-react';
@inject('configureStore')
@observer
export default class EditableTable extends Component {
  constructor(props) {
    super(props);
  }
getColumns(){
// return columns;
}
  render() {
const {testObj} = this.props.configureStore;
const dataSource = testObj.map(item => ({...item}));
    const columns = this.getColumns(dataSource);
    return (
        <Table
          dataSource={dataSource}
          columns={columns}
        /> );
  }
}

这样可以达到让子组件根据observable state改变而re-render的目的,这种方式只是普通的React组件渲染规则,父组件根据observable state的改变而重新渲染,触发子组件重新渲染。

3、间接引用值使用错误

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import Timer from '../Timer';
import './style.css';

@inject( 'timeStore')
@observer
export default class User extends Component{
    constructor(props){
        super(props);
        this.state={};
    }
    render(){
        const {timerData} = this.props.timeStore;
        return(
            <div className='user'>
                <Timer secondsPassed={timerData.secondsPassed} />
            </div>
        );
    }
}

在上面例子中,Timer组件不会对secondsPassed的修改作出反应,因为mobx中值不是 observable,对象的属性才是。

所以,上面例子中,只是 secondsPassed 的当前值传递给了 Timer,这个值是不可变值 ,值永远都不会改变,所以 Timer 组件也永远不会更新。

正确的使用方式应该是Timer组件做如下修改:

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';

@inject('timeStore')
@observer
export default class Timer extends Component{
    constructor(props){
        super(props);
        this.state = {};
    }

    render(){
        const {timerData} = this.props.timeStore;
        return(
            <div className='time_content'>
                <div>Seconds passed:{timerData.secondsPassed}</div>
            </div>
        );
    }
}

这个例子说明mobx中间接引用值应该尽可能的使用。

4、观察者的父子组件re-render并没有直接联系

可能我们已经习惯了React中父组件re-render会影响子组件也跟着re-render,在mobx中,这并不是必然的,观察者组件只会对它观察的observable state作出反应,这是非常高效的,可以避免不必要的组件re-render。但在一些特殊需求面前,也是需要特殊处理的。

先看一个需求,如下图:

要求配置修改后,保存前发布和导出配置为不可操作状态,即:

简化后的代码结构为:

<TabLayout>
  <ToolBar />
  <Configure />
</TabLayout>

即Configure组件的状态集activeItemConfigureData修改后,ToolBar组件需要做出相应的反应,因为ToolBar是一个通用组件,只接收类似于下面的通用配置数据,

[{id: 'release', title: '发布', text: '发布', icon: 'icon_release', onClick: this.handleRelease, disabled: !hasEditPri},
{id: 'save', title: '保存', text: '保存', icon: 'icon_save', onClick: this.handleSave, disabled: !hasEditPri}]

所以能否操作的控制应该在TabLayout中做修改,所以就将activeItemConfigureData stringify之后传递给ToolBar组件,ToolBar不是观察者组件,props更改后会触发ToolBar组件的re-render。

// 将activeData stringify额外传入ToolBar组件,仅仅为了各Type组件修改值后该组件会渲染,
// 会影响效率,但是保证ToolBar组件的联动
const activeItemDataString = JSON.stringify(activeItemConfigureData);
<ToolBar
  activeItemData={activeItemDataString}
/>

这种解决方法能保证实现上述需求。

5、一种不推荐的解决方案

网上可以查到一种强制re-render的方法,主要思想是添加一个比如count的计数器,每个需要强制更新的action中触发另外一个action,使count+1,在需要更新的组件中调用一下count,以此来触发组件的re-render。

上面这几个问题只是我在使用mobx开发时遇到的问题,这里记录一下解决方案。在后面使用时可能还会遇到其它问题,会持续更新,也欢迎补充。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值