store变化了而页面取不到值——mobx会对什么作出反应

1、mobx怎么对可观察对象作出反应

MobX 通常会对你期望的东西做出反应。 这意味着在90%的场景下,mobx “都可以工作”。 然而,在某些时候,你会遇到一个情况,它可能不会像你所期望的那样工作。 在这个时候理解 MobX 如何确定对什么有反应就显得尤为重要。

MobX 会对在 追踪函数执行 过程读取现存的可观察属性做出反应。
  • “读取” 是对象属性的间接引用,可以用过 . (例如 user.name) 或者 [] (例如 user['name']) 的形式完成。
  • “追踪函数” 是 computed 表达式、observer 组件的 render() 方法和 whenreaction 和 autorun 的第一个入参函数。
  • “过程(during)” 意味着只追踪那些在函数执行时被读取的 observable 。这些值是否由追踪函数直接或间接使用并不重要。

换句话说,MobX 不会对其作出反应:

  • 从 observable 获取的值,但是在追踪函数之外
  • 在异步调用的代码块中读取的 observable
  • MobX 追踪属性访问,而不是值

假设你有如下的 observable 数据结构(默认情况下 observable 会递归应用,所以本示例中的所有字段都是可观察的)。

`let message = observable({
    title: "Foo",
    author: {
        name: "Michel"
    },
    likes: [
        "John", "Sara"
    ]
})` Copy

在内存中看起来像下面这样。 绿色框表示可观察属性。 请注意, 本身是不可观察的!
image.png
现在 MobX 基本上所做的是记录你在函数中使用的是哪个箭头。之后,只要这些箭头中的其中一个改变了(它们开始引用别的东西了),它就会重新运行。

2、没有封装成observer 组件

容器组件

import Change from './Change';
import Father from './Father';

const Main = (props: any) => {
  return (
    <div>
      <Father></Father>
      <Change></Change>
    </div>
  )
}

export default Main;

Father组件

import { inject } from 'mobx-react';
import React from 'react';
import Store from '../../store/store';

interface IProps {
  store?: Store;
}

const Father = (props: IProps) => {
  const { store } = props;
  const { message } = store as Store;

  return <div>
    <div>title: {message.title}</div>
    <div>author: {message.author.name}</div>
    <div>likes: {message.likes[0]}</div>
  </div>
}

export default inject('store')(Father);

Change组件

import { Divider } from 'antd';
import { inject, observer } from 'mobx-react';
import React from 'react';
import Store from '../../store/store';

interface IProps {
  store?: Store;
}

const Change = (props: IProps) => {
  const { store } = props;

  const { setName, setTitle, setLikes } = store as Store;

  return <div>
    <button onClick={() => setTitle('spin')}>改变title</button>
    <button onClick={() => setName('tom')}>改变name</button>
    <button onClick={() => setLikes('john')}>改变likes</button>
  </div>
}

export default inject('store')(observer(Change));

store

import { observable, action } from 'mobx';

class Store {
  @observable message = {
    title: 'Bar',
    author: {
      name: 'Susan'
    },
    likes: ['Michel']
  }

  @action
  setTitle = (title: string) => {
    this.message.title = title;
  }
  
  @action
  setName = (name: string) => {
    this.message.author.name = name;
  }

  @action
  setLikes = (target: string) => {
    this.message.likes[0] = target;
  }
}

export default Store;

当点击改变title、name、likes按钮时,store中观察对象message的属性值改变了,但是Father组件并没有重新渲染。因为Father组件并不是observer组件,只有封装成observer组件,mobx才会对render函数(函数组件理解为return返回的ReactNode)中读取现存的可观察属性做出反应。

因此,只需要将Father组件封装成observer组件就可以解决

export default inject('store')(Father);
改后
export default inject('store')(observer(Father));

3、MobX 只会为数据是直接通过 render 存取的 observer 组件进行数据追踪

Father组件

const Father = (props: IProps) => {
  const { store } = props;
  const { message } = store as Store;

  return <div>
    <div>title: {message.title}</div>
    <Child title={() => <div>{message.author.name}</div>}></Child>
    <div>likes: {message.likes[0]}</div>
  </div>
}

export default inject('store')(observer(Father));

Child组件

interface IProps {
  title: () => React.ReactNode;
}

const Child = (props: IProps) => {
  const { title } = props;

  return <div>{title()}</div>
}

export default Child;

当改变storemessage.author.name时,页面并不会重新渲染。因为div实际上不是由 Father(有追踪的渲染) 渲染的,而是 Child。 所以要确保 Child 的 title 可以正确对新的 message.author.name 作出反应,Child 应该也是一个 observer

如果 Child 来源于外部库的话,这通常不在你的掌控之中。在这种场景下,你可以用自己的无状态 observer 组件来包裹 div 解决此问题,或通过利用 <Observer>组件:

// 将Child改成observer组件
const Child = (props: IProps) => {
  const { title } = props;

  return <div>{title()}</div>
}

export default observer(Child);

另外一种方法可以使用 mobx-react 内置的 Observer 组件,它不接受参数,只需要单个的 render 函数作为子节点:

const Father = (props: IProps) => {
  const { store } = props;
  const { message } = store as Store;

  return <div>
    <div>title: {message.title}</div>
    <Child title={() => <Observer>{() => <div>{message.author.name}</div>}</Observer>}></Child>
    <div>likes: {message.likes[0]}</div>
  </div>
}

4、在本地字段中缓存 observable

一个常见的错误就是把间接引用的 observable 存储到本地变量,然后认为组件会作出反应。举例来说:

@inject('store')
@observer
class Father extends React.Component<IProps, any> {
  author: { name: string; } | undefined;
  likes: string[] | undefined;
  title: string | undefined;

  constructor(props: IProps) {
    super(props);
    this.title = props.store?.message.title;
    this.author = props.store?.message.author;
    this.likes = props.store?.message.likes;
    console.log('title: ' ,this.title, 'author: ' ,this.author, 'likes: ', this.likes)
  }

  render() {
    return <div>
      <div>title: {this.title}</div>
      <div>name: {this.author?.name}</div>
      <div>likes: {this.likes && this.likes[0]}</div>
    </div>
  }
}

export default Father;

组件会对authorlikes做出反应,不会对title做出反应,因为this.title = props.store?.message.title;是赋值,而this.author = props.store?.message.author;是赋引用,这个引用也是一个observable对象
image.png

换成函数组件

const Father = (props: IProps) => {
  const { store } = props;
  const { message } = store as Store;

  const title = message.title;
  const author = message.author;
  const likes = message.likes;

  return <div>
    <div>title: {title}</div>
    <Child title={() => <Observer>{() => <div>{author.name}</div>}</Observer>}></Child>
    <div>likes: {likes[0]}</div>
  </div>
}

export default inject('store')(observer(Father));

发现组件即会对authorlikes做出反应,也会对title做出反应

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要监听 localStorage 变化,可以使用`window.addEventListener('storage', callback)`方法。这个方法在 localStorage 中发生变化时触发回调函数。 在 MobX 中,可以使用`autorun`函数来监听 localStorage 的变化。`autorun`函数自动追踪被观察的数据,并在它们发生变化时执行回调函数。 以下是一个使用 MobX 监听 localStorage 的例子: ```javascript import { autorun } from 'mobx'; // 定义一个 observable 对象 const storage = { value: localStorage.getItem('value') || '', setValue(newValue) { this.value = newValue; localStorage.setItem('value', newValue); }, }; // 使用 autorun 监听 storage.value 的变化 autorun(() => { console.log(`localStorage 中的 value 发生变化:${storage.value}`); }); // 修改 localStorage 中的 value storage.setValue('new value'); ``` 在这个例子中,我们先定义了一个 observable 对象 `storage`,它有一个属性 `value` 表示 localStorage 中的。`setValue` 方法用于修改 `value` ,并将修改后的存储到 localStorage 中。 然后我们使用 `autorun` 函数来监听 `storage.value` 的变化。当 `storage.value` 发生变化时,`autorun` 函数自动执行回调函数,输出变化后的。 最后我们调用 `storage.setValue('new value')` 修改了 localStorage 中的 value ,这时 `autorun` 函数自动执行回调函数,输出新的。 注意:`autorun` 函数在组件挂载时自动执行一次回调函数,因此在上面的例子中,在控制台中先输出 localStorage 中的 value

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值