NervJS / taro

开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发微信/京东/百度/支付宝/字节跳动/ QQ 小程序/H5/React Native 等应用。 https://taro.zone/
https://docs.taro.zone/
Other
35.46k stars 4.78k forks source link

renderProps渲染异常 #4150

Closed John251314 closed 5 years ago

John251314 commented 5 years ago

问题描述 自定义组件使用renderProps进行循环渲染,只能显示最后一个组件

复现步骤 1、封装自定义组件A和B 2、在A中使用renderProps,循环渲染组件B 3、只会渲染出来最后一次调用renderProps生成的组件,前几个不能出现,且renderProps不能传递参数

// 封装的Grid组件,调用renderProps生成包含B组件的列表
    renderRow(rowData: Data[], index: number) {
        const { renderItem } = this.props;
        return (
            <View key={index.toString()} className={styles.row}>
                {rowData.map((data, key) => {
                    console.log('Grid组件调用renderRow data is ' + data)
                    return (
                        <View key={key.toString()} className={styles.item}>
                            {renderItem(data)}
                        </View>
                    )
                })}
            </View>
        );
    }
// 调用文件

    renderGrid() {
        return (
            <View>
                <Grid
                    columnNum={2}
                    data={[1, 2, 3, 4]}
                    renderItem={message => {
                        console.log('renderItem + ', message);
                        return <View>renderItem{message}</View>
                    }}
                />
            </View>
        );
    };

期望行为 理论上可以通过调用renderProps实现自定义渲染子组件列表,结果只渲染出一个

报错信息

系统信息

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

补充信息 [可选] 通过log可以看出,在map调用renderProps之前,renderProps已经先自动调用,并传递参数为undefined;然后才map执行renderProps,但并未渲染

log 如下:

image

Grid组件如下:

/**
 *  Created by ljh on 2019/08/12.
 */

import Taro from '@tarojs/taro';
import { View } from '@tarojs/components';
import styles from './Grid.less';

type Data = any;

interface Props {
    columnNum: number;
    data: Data[];
    renderItem: (data: Data) => any;
}

export default class Grid extends Taro.Component<Props> {

    static defaultProps = {
        data: [],
        columnNum: 1,
    };

    render() {
        const { columnNum, data } = this.props;
        const num = Math.max(columnNum, 1);
        const rowNum = Math.ceil(data.length / num);
        const rows = [...Array(rowNum).keys()];
        return (
            <View className={styles.container}>
                {rows.map((index) => {
                    const start = index * rowNum;
                    const end = start + rowNum;
                    const items = data.slice(start, end);
                    return this.renderRow(items, index);
                })}
            </View>
        );
    }

    renderRow(rowData: Data[], index: number) {
        const { renderItem } = this.props;
        return (
            <View key={index.toString()} className={styles.row}>
                {rowData.map((data, key) => {
                    console.log('Grid组件调用renderRow data is ' + data)
                    return (
                        <View key={key.toString()} className={styles.item}>
                            {renderItem(data)}
                        </View>
                    )
                })}
            </View>
        );
    }
}

html结构如下:

image

应该在每个item下插入一个<View>renderItem</View>的,结果插入了和grid->container平级的地方,而且只有一个

taro-bot[bot] commented 5 years ago

欢迎提交 Issue~

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

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

Good luck and happy coding~

yuche commented 5 years ago

微信小程序里循环不能用 render props 以及 children 与组合,这点在文档有说明的。其他大部分小程序和 h5 没有这个限制。

John251314 commented 5 years ago

微信小程序里循环不能用 render props 以及 children 与组合,这点在文档有说明的。其他大部分小程序和 h5 没有这个限制。

好吧,这都是react常规操作啊~,怪我没有仔细读文档

yuche commented 5 years ago

主要是微信小程序的 slot 机制有 bug,这问题长久以来一直没有解决,也不知道微信怎么想的

taro-bot[bot] commented 5 years ago

Hello~

您的问题楼上已经有了确切的回答,如果没有更多的问题这个 issue 将在 15 天后被自动关闭。

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

Good luck and happy coding~

Jokcy commented 5 years ago

@yuche 现在renderProps不能以方法调用的方式使用么?比如下面:

export default function Page(props) {
  return <View>{props.renderPage('asd')}</View>
}

这样写小程序端不能用,会编译成

<block a:if="{{$taroCompReady}}">
    <view>{{anonymousState__temp}}</view>
</block>

然后在_createData里面执行renderPage,但是根据文档 https://nervjs.github.io/taro/docs/render-props.html 说应该是可以的,这里是什么问题呢?

Taro版本是1.3.12

yuche commented 5 years ago

尝试下

export default function Page({ renderFuck }) {
  return <View>{renderFuck('asd')}</View>
}
Jokcy commented 5 years ago

@yuche 这样居然可以。。。这感觉跟 https://nervjs.github.io/taro/docs/children.html 的文档有点冲突啊。还有一点,传递的参数似乎只能是state,不能是随便定义的变量(这点倒也可以理解)。

renderFuck。。。看样子你们也被这个折腾挺烦的。

yuche commented 5 years ago

@yuche 这样居然可以。。。这感觉跟 nervjs.github.io/taro/docs/children.html 的文档有点冲突啊。还有一点,传递的参数似乎只能是state,不能是随便定义的变量(这点倒也可以理解)。

renderFuck。。。看样子你们也被这个折腾挺烦的。

没有没有,这是我个人最喜欢的临时变量,因为这个名字不会冲突…… 你可以把这个问题在新的 issue 里一并提了。

imaxing commented 4 years ago

尝试下

export default function Page({ renderFuck }) {
  return <View>{renderFuck('asd')}</View>
}

@Jokcy 请问这个怎么使用? 我新建这个函数调用报错, renderFuck 是传递的什么参数? 自定义 render 函数吗? 这样传递报错 image