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

Props 传递 Class 渲染异常 #4059

Closed
sheyunsi opened this issue Aug 2, 2019 · 10 comments
Closed

Props 传递 Class 渲染异常 #4059

sheyunsi opened this issue Aug 2, 2019 · 10 comments
Assignees

Comments

@sheyunsi
Copy link

sheyunsi commented Aug 2, 2019

问题描述
父组件使用map渲染一个数组,数组中的元素都是 class,子组件在接受到 props 以后能正常渲染。 但是当这个数组被重新排序以后,子组件的渲染就出现问题了,并且还修改了父组件的 state

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

  1. 初始化渲染一切正常;
  2. 点击父组件按钮 Shufflethis.state.source 进行重新排序;
  3. 观察渲染结果 和 控制台打印结果,会发现 setState 之后的数据 和 渲染完之后的数据并不一样,子组件在渲染的过程中,把父组件的 state 给修改了。

父组件:

import Taro from '@tarojs/taro'
import { View, Button } from "@tarojs/components";
import Item from "./Item";

function random(min, max) {
    if (max == null) {
        max = min;
        min = 0;
    }
    return min + Math.floor(Math.random() * (max - min + 1));
}

function arrayShuffle(array: Array<any>) {
    let length = array.length,
        shuffled = Array(length);
    for (let index = 0, rand; index < length; index++) {
        rand = random(0, index);
        if (rand !== index) shuffled[index] = shuffled[rand];
        shuffled[rand] = array[index];
    }
    return shuffled;
}

interface IProduct {
    ID: string,
    name: string,
}

class Product implements IProduct {
    ID: string;
    name: string;

    get localized(): string {
        return "localized" + this.name;
    }

    constructor(ID, name) {
        this.ID = ID;
        this.name = name;
    }

}

export class Profile extends Taro.Component<{}, { source: IProduct[] }> {

    constructor() {
        super();
        this.state = {
            source: [
                new Product("1", "name1"),
                new Product("2", "name2"),
                new Product("3", "name3"),
                new Product("4", "name4"),
            ]
        }
    }

    shuffle = () => {
        const shuffled = arrayShuffle(this.state.source);
        console.log("--------- shuffled ----------");
        shuffled.forEach(item => {
            console.log(item.name)
        });

        this.setState(
            { source: shuffled },
            () => {
                console.log("--------- source ----------");
                this.state.source.forEach(item => {
                    console.log(item.name)
                });
            }
        )
    };

    render() {
        return (
            <View>
                <Button onClick={ this.shuffle }>Shuffle</Button>
                { this.state.source.map(item => <Item key={ item.ID } product={ item }/>) }
            </View>
        )
    }

}

子组件:

import Taro from "@tarojs/taro";
import { View } from "@tarojs/components";

export default class Item extends Taro.Component<{ product: any }> {

   componentWillReceiveProps(nextProps: Readonly<{ product: any }>): void {
        console.log("======== Item Receive Props =========");
        console.log("props: ", this.props.product);
        console.log("nextProps: ", nextProps.product);
    }

    render() {
        return <View>{ this.props.product.name }</View>
    }
}

系统信息
👽 Taro v1.3.11

Taro CLI 1.3.11 environment info:
System:
OS: macOS 10.14.5
Shell: 5.3 - /bin/zsh
Binaries:
Node: 12.4.0 - ~/.nvm/versions/node/v12.4.0/bin/node
Yarn: 1.16.0 - /usr/local/bin/yarn
npm: 6.9.0 - ~/.nvm/versions/node/v12.4.0/bin/npm
npmPackages:
@tarojs/components: 1.3.9 => 1.3.9
@tarojs/plugin-babel: 1.3.9 => 1.3.9
@tarojs/plugin-csso: 1.3.9 => 1.3.9
@tarojs/plugin-less: 1.3.9 => 1.3.9
@tarojs/plugin-uglifyjs: 1.3.9 => 1.3.9
@tarojs/router: 1.3.9 => 1.3.9
@tarojs/taro: 1.3.9 => 1.3.9
@tarojs/taro-alipay: 1.3.9 => 1.3.9
@tarojs/taro-h5: 1.3.9 => 1.3.9
@tarojs/taro-swan: 1.3.9 => 1.3.9
@tarojs/taro-tt: 1.3.9 => 1.3.9
@tarojs/taro-weapp: 1.3.9 => 1.3.9
@tarojs/webpack-runner: 1.3.9 => 1.3.9
eslint-config-taro: 1.3.9 => 1.3.9
eslint-plugin-taro: 1.3.9 => 1.3.9
nerv-devtools: ^1.4.0 => 1.4.3
nervjs: ^1.4.0 => 1.4.3
stylelint-config-taro-rn: 1.3.9 => 1.3.9
stylelint-taro-rn: 1.3.9 => 1.3.9

补充信息
经过排查,可以确定是在传进 props 后被修改的,可以在子组件的 componentWillReceiveProps 中查看。
如果将 props 序列化之后再传递的是可以的。

@taro-bot
Copy link

taro-bot bot commented Aug 2, 2019

欢迎提交 Issue~

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

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

Good luck and happy coding~

@taro-bot
Copy link

taro-bot bot commented Aug 4, 2019

CC @Chen-jj

@Chen-jj
Copy link
Contributor

Chen-jj commented Aug 5, 2019

@sheyunsi CLI 和依赖版本保持一致。

image

image

这样的 log 有问题?

@sheyunsi
Copy link
Author

sheyunsi commented Aug 5, 2019

@Chen-jj
不好意思,我这边 cli 和 依赖库 同步升级了一下。 都是 1.3.12 了。

您上面发的log是没有问题的,但是我这边测试的还是有问题,我贴下我的log:
image
image

想知道您那边也是 1.3.11 或 1.3.12 吗?

@Chen-jj
Copy link
Contributor

Chen-jj commented Aug 5, 2019

@sheyunsi 我本地开发所以是 link 来用的,一直保持最新版。你可以删 node_modules 重装试试。

@sheyunsi
Copy link
Author

sheyunsi commented Aug 5, 2019

@Chen-jj 我删了重装过了,还是这样。您看如果方便的话,我可以打个包,把代码发给您看看。

@Chen-jj
Copy link
Contributor

Chen-jj commented Aug 5, 2019

@sheyunsi 也行吧,你把代码精简下放 github。建议再试试新建一个项目,跑你的这段代码试试。

@taro-bot
Copy link

taro-bot bot commented Aug 12, 2019

Hello~

您的问题我们无法复现。如果有空的话还请拔冗提供一个简单的复现 demo,否则这个 issue 将在 15 天后被自动关闭。

如果您在这 15 天中更新更多信息自动关闭的流程会自动取消,如有其他问题也可以发起新的 Issue。

Good luck and happy coding~

@Chen-jj
Copy link
Contributor

Chen-jj commented Aug 16, 2019

@sheyunsi 老哥我今天再尝试了一下可以复现了。之前图快没有使用 Product 类构建 this.state.source,而是直接写的对象字面量。

debug 后发现,是一个较神奇的问题。Taro 为了减少每次 setData 的量,会先 diff 将要 setData 的数据和当前组件的 data 数据,然后再按路径去 setData。

然后我尝试了三种思路,能绕过问题:

  1. 不 diff,不按路径 setData,而是完整 setData 整个新数据对象。
  2. diff 后的数据做深拷贝:setData(JSON.parse(JSON.stringify(dataDiff)))
  3. 使用对象字面量,而不是用 Product 类创建 state 的数据。

猜测小程序底层虚拟 dom 的 diff 会影响到 props 的数据。

暂时没有什么好的解决办法,去掉 diff 肯定和对 diff 数据做深拷贝对性能都有很大影响,所以建议换用对象字面量。

@Chen-jj
Copy link
Contributor

Chen-jj commented Sep 5, 2019

已修复

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

3 participants