Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

微信小程序, 列表数据中有 new ClassType() 的对象时, 并且向数组前面添加数据时, 该对象在列表中超大概率显示错乱 #4350

Closed
deng-yc opened this issue Aug 30, 2019 · 13 comments
Assignees

Comments

@deng-yc
Copy link

deng-yc commented Aug 30, 2019

问题描述
小程序中, 列表数据中有 new ClassType() 的对象时, 并且向数组前面添加数据时, 该对象在列表中超大概率显示错乱

复现步骤
[复现问题的步骤]

  1. 点击刷新,使列表有初始数据
  2. 点击加载更多,向列表前面添加数据
  3. 列表中的第三列数据为 new DataItem()的数据,显示会出问题

mobx 模版

index.tsx

@inject('myStore')
@observer
class Index extends Component {
  config: Config = {
    navigationBarTitleText: '首页'
  };

  refresh = () => {
    const { myStore } = this.props;
    myStore.refresh();
  };

  loadmore = () => {
    const { myStore } = this.props;
    myStore.loadmore();
  };

  render() {
    const { myStore } = this.props;
    const { messageKeys, messageData, status } = myStore;
    return (
      <View className="index">
        <Button onClick={this.loadmore}>加载更多</Button>
        {status == 'loading' && <View>loading</View>}
        {messageKeys.map(msgKey => {
          const data = messageData[msgKey];
          return (
            <View key={msgKey}>
              {msgKey} - {data.id} - {data.item.id}
            </View>
          );
        })}
        <Button onClick={this.refresh}>刷新</Button>
      </View>
    );
  }
}

myStore.ts

import { observable } from 'mobx';
type LoadingStatus = 'none' | 'loading' | 'success' | 'fail';
let temp_id = 0;
function getNextId() {
  temp_id += 1;
  return temp_id;
}
class DataItem {
  constructor(public id) {}
}
export class MyStore {
  @observable status: LoadingStatus = 'none';

  messageKeys: string[] = [];
  messageData = {};

  async refresh() {
    temp_id = 0;
    this.status = 'loading';
    const tempKeys: string[] = [];
    for (let i = 0; i < 5; i++) {
      const key = `id-${getNextId()}`;
      const item = new DataItem('item-' + key);
      this.messageData[key] = {
        id: `data-` + key,
        item
      };
      tempKeys.push(key);
    }
    this.messageKeys = tempKeys;
    this.status = 'success';
    console.log('刷新成功', this.messageKeys);
  }

  async loadmore() {
    this.status = 'loading';
    const tempKeys: string[] = [];
    for (let i = 0; i < 5; i++) {
      const key = `id-${getNextId()}`;
      const item = new DataItem('item-' + key);
      this.messageData[key] = {
        id: `data-` + key,
        item
      };
      tempKeys.push(key);
    }
    //将新的数据加入到列表前面,  item的内容会乱掉
    this.messageKeys = [...tempKeys, ...this.messageKeys];
    this.status = 'success';
    console.log('加载成功', this.messageKeys);
  }
}

export const store = new MyStore();

export default store;

期望行为
期望new的对象也能正确显示

报错信息
image

系统信息

Taro v1.2 及以上版本已添加 taro info 命令,方便大家查看系统及依赖信息,运行该命令后将结果贴下面即可。

  • 操作系统: [Windows 10]
  • Taro 版本 [1.3.15]
  • Node.js 版本 [10.16.3]
  • 报错平台 [weapp]

补充信息
[可选]
[根据你的调查研究,出现这个问题的原因可能在哪里?]

@taro-bot
Copy link

taro-bot bot commented Aug 30, 2019

欢迎提交 Issue~

如果你提交的是 bug 报告,请务必遵循 Issue 模板的规范,尽量用简洁的语言描述你的问题,最好能提供一个稳定简单的复现。🙏🙏🙏

如果你的信息提供过于模糊或不足,或者已经其他 issue 已经存在相关内容,你的 issue 有可能会被关闭。

Good luck and happy coding~

@deng-yc
Copy link
Author

deng-yc commented Aug 31, 2019

问题描述
另外发现 map 你们使用自定义组件,也会出现显示错乱的问题

复现步骤
[复现问题的步骤]

  1. 点击刷新,使列表有初始数据
    2 .点击加载更多,向列表前面添加数据
    3 .列表中数据显示错误

index.tsx

import { Button, Text, View } from '@tarojs/components';
import { inject, observer } from '@tarojs/mobx';
import Taro, { Component, Config } from '@tarojs/taro';
import { ComponentType } from 'react';
import { MyStore } from 'src/store/mystore';
import Test from './components/test';
import './index.less';
type PageStateProps = {
  myStore: MyStore;
};
interface Index {
  props: PageStateProps;
}
@inject('myStore')
@observer
class Index extends Component {
  config: Config = {
    navigationBarTitleText: '首页'
  };

  refresh = () => {
    const { myStore } = this.props;
    myStore.refresh();
  };

  loadmore = () => {
    const { myStore } = this.props;
    myStore.loadmore();
  };

  renderItem(data){
      return <View>{data}</View>
  }

  render() {
    const { myStore } = this.props;
    const { messageKeys, status } = myStore;
    return (
      <View className="index">
        <Button onClick={this.loadmore}>加载更多</Button>
        {status == 'loading' && <View>loading</View>}
        {messageKeys.map(msgKey => {
          return (
            <View key={msgKey}>
              <Test data={msgKey} />
            </View>
          );
        })}
        <Button onClick={this.refresh}>刷新</Button>
      </View>
    );
  }
}

export default Index as ComponentType;

components/test.tsx

import { View } from '@tarojs/components';
import Taro, { Component } from '@tarojs/taro';
import './index.less';
type CompProps = {
  data: string
};
class Test extends Component<CompProps> {
  render() {
    const { data } = this.props;
    return <View>{data}</View>;
  }
}

export default Test;

mystore.ts

import { observable } from 'mobx';
type LoadingStatus = 'none' | 'loading' | 'success' | 'fail';
let temp_id = 0;
function getNextId() {
  temp_id += 1;
  return temp_id;
}
export class MyStore {
  @observable status: LoadingStatus = 'none';

  messageKeys: string[] = [];
  async refresh() {
    temp_id = 0;
    this.status = 'loading';
    const tempKeys: string[] = [];
    for (let i = 0; i < 5; i++) {
      const key = `id-${getNextId()}`;
      tempKeys.push(key);
    }
    this.messageKeys = tempKeys;
    this.status = 'success';
    console.log('刷新成功', this.messageKeys);
  }

  async loadmore() {
    this.status = 'loading';
    const tempKeys: string[] = [];
    for (let i = 0; i < 5; i++) {
      const key = `id-${getNextId()}`;
      tempKeys.push(key);
    }
    //将新的数据加入到列表前面,  item的内容会乱掉
    this.messageKeys = [...tempKeys, ...this.messageKeys];
    this.status = 'success';
    console.log('加载成功', this.messageKeys);
  }
}

export const store = new MyStore();

export default store;

错误截图

image

系统信息

Taro v1.2 及以上版本已添加 taro info 命令,方便大家查看系统及依赖信息,运行该命令后将结果贴下面即可。

操作系统: [Windows 10]
Taro 版本 [1.3.15]
Node.js 版本 [10.16.3]
报错平台 [weapp]

@huey-LS
Copy link
Contributor

huey-LS commented Sep 2, 2019

我这也有遇到... 希望能尽快修复,现在开发很难受

@taro-bot
Copy link

taro-bot bot commented Sep 2, 2019

CC @Chen-jj

@Chen-jj
Copy link
Contributor

Chen-jj commented Sep 3, 2019

#4059 #4344
猜想和这两者是同样的问题,微信小程序循环渲染时的 dom diff 会对 data 造成影响。

可以到 dist/npm/@tarojs/taro-weapp/dist/index.js 中,找到 component.$scope.setData(dataDiff, cb) 这里。

试试下面两种方法:

  1. dataDiff 改为 data,不用 diff 后的数据。
  2. setData 前先序列化 setData 的数据:JSON.parse(JSON.stringify(dataDiff))

@deng-yc
Copy link
Author

deng-yc commented Sep 3, 2019

@Chen-jj 两种方法都试过了, 并没有解决问题

@deng-yc
Copy link
Author

deng-yc commented Sep 4, 2019

@Chen-jj
image

问题应该出在这里, 生成compid 的 时候用了_anonIdx去得到一个新的compid,
所以如果往前面加元素, 新的组件得到的compid会是一个已经存在的compid,而前面已经存在的组件得到一个新的compid
我把他改成item.$original 去获取compid顺序就正确了
如果解决方法是正确的,希望尽快更新,谢谢了

@Chen-jj
Copy link
Contributor

Chen-jj commented Sep 4, 2019

@deng-yc 关于第一点,应该和 #4059 是一样的,微信的循环渲染会影响到 data 中以 new 创建的对象。

下面打印的是点击“加载更多”时的 diff 后,真正 setData 的数据,console.log(JSON.stringify(dataDiff))。可以看到 loopArray0[5] ~ loopArray0[9] 内的 data.item.id 分别对应 1~5。证明 Taro 传给 setData 的数据是正确的,但如果你直接打印 dataDiff 可以看到 setData 后数据会被小程序所修改了。

image

支付宝运行正常也证明这个猜想。

image

所以建议不要用 new,或者有空你可以写份原生微信小程序试一试。

@Chen-jj
Copy link
Contributor

Chen-jj commented Sep 5, 2019

@deng-yc 第二点,问题是出在 compid,但和你的猜想有点不同。这个下个版本修复。

@deng-yc
Copy link
Author

deng-yc commented Sep 5, 2019

关于第一个问题,我跟踪了diffArrToPath 和diffObjToPath,
经过调试后猜测
点击加载更多的时候,对比后会得到

//前5个对象因为有from和to进行比较, 将得到 dataDiff

'loopArray0[0].data.id' = 新加入的对象的ID
...
//后面的5个对象由于 to没有了,会得到对象的data
'loopArray0[4].data'=new DataItem 对象
...

这个时候交给微信setData的,
前面的5个通过路径修改之前new出来的 DataItem的id,
后面的5个的data,在数据中也是之前new出来的DataItem对象 其实和前面的5个的data和后面5个的data是同一个对象
这个应该是微信setData 用路径更新有问题

所以我觉得在对比的时候,判断一下如果是new的对象,就不继续往下对比

@deng-yc
Copy link
Author

deng-yc commented Sep 5, 2019

我在diffObjToPath和diffArrToPath 加了以下代码试了下, new 的数据不会乱了
image

@Chen-jj
Copy link
Contributor

Chen-jj commented Sep 5, 2019

关于第一个问题,我跟踪了diffArrToPath 和diffObjToPath,
经过调试后猜测
点击加载更多的时候,对比后会得到

//前5个对象因为有from和to进行比较, 将得到 dataDiff

'loopArray0[0].data.id' = 新加入的对象的ID
...
//后面的5个对象由于 to没有了,会得到对象的data
'loopArray0[4].data'=new DataItem 对象
...

这个时候交给微信setData的,
前面的5个通过路径修改之前new出来的 DataItem的id,
后面的5个的data,在数据中也是之前new出来的DataItem对象 其实和前面的5个的data和后面5个的data是同一个对象
这个应该是微信setData 用路径更新有问题

所以我觉得在对比的时候,判断一下如果是new的对象,就不继续往下对比

我在diffObjToPath和diffArrToPath 加了以下代码试了下, new 的数据不会乱了
image

@deng-yc 的确如此,感谢指出!

@Chen-jj Chen-jj closed this as completed in ae66b35 Sep 5, 2019
@zzyhdu
Copy link

zzyhdu commented Sep 16, 2019

nb!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants