我已经使用了动态状态为每个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或 componentDidUpdate。 React将嵌套更新的数量限制为 防止无限循环。
答案 0 :(得分:3)
在这里使用currying概念,像这样:
public handleClick = (index : any) = () => {
this.setState({ [index]: true });
}
并以相同的方式使用handleClick:onClick={props.handleClick(index)}
请查看此答案以获取更多详细信息:What is 'Currying'?
答案 1 :(得分:0)
此问题是onClick
需要一个可以处理参数MouseEvent<T> | undefined
的函数。但是,调用onClick={handleClick(index)}
解析为handleClick(index)
在渲染上解析,并且渲染为onClick={undefined}
。
您需要将onClick处理程序更改为onClick={() => handleClick(index))
或将handleClick更改为handleClick = (index) => () => { ... }
。
}
第一个选项将标记tslint jsx-no-lambda
规则,在这种情况下,您需要使用第二个选项。
尽管应该禁用jsx-no-lamda
,但该规则的目的是因为可能会对性能产生影响,因为每个渲染都会创建一个新功能(在Why shouldn't JSX props use arrow functions or bind?中进行了讨论)。但是,这会使代码更难以推理。通常,过早地优化代码以牺牲可读性为代价是不好的。
如果您以后发现昂贵的渲染可以从此优化中受益,那么最好采用可读的解决方案,例如使用memobind
这样的库来进行记忆