【问题标题】:React Typescript Infinite loop with render from function使用函数渲染反应 Typescript 无限循环
【发布时间】:2018-09-04 10:47:23
【问题描述】:

我使用了动态状态来为每个 ListItem 提供一个单独的状态,以便折叠是否打开。但是,由于我必须将参数传递给 handleClick 函数,因此我的渲染正在经历一个无限循环。

我得到的错误是

Lambdas are forbidden in JSX attributes due to their rendering performance impact

它不允许我使用 Lambda,所以我不能这样做

<ListItem button={true} onClick={() => props.handleClick(index)}>

我想知道是否有办法解决这个问题,这样我就可以让每个列表项的 handleClick 及其索引值,而不会经历无限循环

App.tsx

interface IState {
error: any,
intro: any,
threads: any[],
title: any,
}

export default class App extends React.Component<{}, IState> {
    constructor (props : any) {
        super (props);

        this.state = {
            error: "",
            intro: "Welcome to RedQuick",
            threads: [],
            title: ""
        };

        this.getRedditPost = this.getRedditPost.bind(this)
        this.handleClick = this.handleClick.bind(this)
    }

    public getRedditPost = async (e : any) => {
        e.preventDefault();

        const subreddit = e.target.elements.subreddit.value;
        const redditAPI = await fetch('https://www.reddit.com/r/'+ subreddit +'.json');
        const data = await redditAPI.json();

        console.log(data);

        if (data.kind) {
            this.setState({
                error: undefined,
                intro: undefined,
                threads: data.data.children,
                title: data.data.children[0].data.subreddit.toUpperCase()
            });
        } else {
            this.setState({
                error: "Please enter a valid subreddit name",
                intro: undefined,
                threads: [],
                title: undefined
            });
        }
    }

    public handleClick = (index : any)  => {
        this.setState({ [index]: true });
    }

    public render() {
        return (
            <div>
                <Header 
                    getRedditPost={this.getRedditPost}
                />
                <p className="app__intro">{this.state.intro}</p>
                {
                    this.state.error === "" && this.state.title.length > 0 ?
                    <LinearProgress />:
                    <ThreadList 
                        error={this.state.error}
                        handleClick={this.handleClick}
                        threads={this.state.threads}
                        title={this.state.title}
                    />
                }   
            </div>
        );
    }
}

Threadlist.tsx

<div className="threadlist__subreddit_threadlist">
    <List>
        { props.threads.map((thread : any, index : any) => 
            <div key={index} className="threadlist__subreddit_thread">
                <Divider />
                <ListItem button={true} onClick={props.handleClick(index)}/* component="a" href={thread.data.url}*/ >
                    <ListItemText primary={thread.data.title} secondary={<p><b>Author: </b>{thread.data.author}</p>} />
                    {props[index] ? <ExpandLess /> : <ExpandMore />}
                </ListItem>
                <Collapse in={props[index]} timeout="auto" unmountOnExit={true}>
                    <p>POOP</p>
                </Collapse>
                <Divider />
            </div>
        ) }
    </List> 
</div>

错误:

已超过最大更新深度。这可能发生在组件 在 componentWillUpdate 内重复调用 setState 或 组件更新。 React 将嵌套更新的数量限制为 防止无限循环。

【问题讨论】:

  • 问题在这里:onClick={props.handleClick(index),分配一个函数而不是值,像这样:onClick={() =&gt; props.handleClick(index)} 或使用柯里化概念:public handleClick = (index : any) = () =&gt; { this.setState({ [index]: true }); }
  • 查看此答案以获取更多详细信息。 Maximum update depth exceeded
  • 检查this问题并讨论ios。
  • 如果我尝试分配一个函数,它会给我一个错误提示“由于其渲染性能影响,在 JSX 属性中禁止使用 Lambda”

标签: reactjs typescript render


【解决方案1】:

在这里使用柯里化概念,像这样:

public handleClick = (index : any) = () => {
    this.setState({ [index]: true });
}

并以同样的方式使用handleClick:onClick={props.handleClick(index)}

查看此答案了解更多详情:What is 'Currying'?

【讨论】:

  • 你的意思是 public handleClick = (index : any) => () => { ... } ?
  • 不,谢谢,它修复了错误,但现在有另一个问题
  • 分享问题,也会找到解决方案:)
  • 所以一切正常,但点击无法折叠,我在 ThreadList.tsx 中有 console.log(props[index]) 但首先它是未定义的,也许是问题?
  • 它是因为你没有在 props 中从父级传递任何索引,你在这里分配的(预期)值是什么:in={props[index]}??
【解决方案2】:

这个问题是onClick 需要一个可以处理MouseEvent&lt;T&gt; | undefined 参数的函数。但是调用onClick={handleClick(index)} 解析为意味着handleClick(index) 在渲染时被解析并呈现为onClick={undefined}

您需要将 onClick 处理程序更改为 onClick={() =&gt; handleClick(index)) 或将 handleClick 更改为 handleClick = (index) =&gt; () =&gt; { ... }。 }

第一个选项会标记 tslint jsx-no-lambda 规则,在这种情况下,您需要使用第二个选项。

尽管禁用jsx-no-lamda 可能值得考虑,但该规则背后的目的是因为每次渲染都会创建一个新函数(在Why shouldn't JSX props use arrow functions or bind? 中讨论)可能会对性能产生影响。但是,它会使代码更难推理。通常,以牺牲可读性为代价过早地优化代码被认为是不好的。

如果您后来发现昂贵的渲染会从这种优化中受益,那么您最好使用可读的解决方案,例如使用诸如 memobind 之类的库来记忆它

【讨论】:

    猜你喜欢
    • 2010-12-24
    • 2021-10-21
    • 1970-01-01
    • 2020-09-11
    • 2021-10-20
    • 2022-10-23
    • 2021-10-05
    • 2021-04-06
    • 2021-07-16
    相关资源
    最近更新 更多