我很难弄清为什么我的应用程序要进行无休止的渲染。
在内部,我的有状态组件,我正在componentDidMount方法中调用redux动作(调用componentWillMount也可以进行无尽渲染)
class cryptoTicker extends PureComponent {
componentDidMount() {
this.props.fetchCoin()
// This fetches some 1600 crypto coins data,Redux action link for the same in end
}
render() {
return (
<ScrollView>
<Header />
<View>
<FlatList
data={this.state.searchCoin ? this.displaySearchCrypto : this.props.cryptoLoaded}
style={{ flex: 1 }}
extraData={[this.displaySearchCrypto, this.props.cryptoLoaded]}
keyExtractor={item => item.short}
initialNumToRender={50}
windowSize={21}
removeClippedSubviews={true}
renderItem={({ item, index }) => (
<CoinCard
key={item["short"]}
/>
)}
/>
</View>
</ScrollView>
)
}
}
在CoinCard中,我实际上不做任何事情(在Flat列表内注意CoinCard)
class CoinCard extends Component {
render () {
console.log("Inside rende here")
return (
<View> <Text> Text </Text> </View>
)
}
}
现在,当我在我的硬币卡渲染器中进行控制台登录时,我可以看到在这里的rende内部
的无限日志[问题:] ?有人可以帮我弄清楚为什么会发生这种情况吗?
您可以click here to see my actions和click here to see my reducer。
[更新:] 我的repository is here(如果要克隆并自己查看)。
[更新:2] :我已经在github上推送了上面的共享代码,它仍然会记录无尽的console.log语句(如果可以克隆run and move back to this commit)。
[更新:3]:我也不再在<ScrollView />
中使用<FlatList />
,当我指的是无尽的渲染时,我的意思是说它是无尽的(并且不必要)将相同的道具传递给子组件(<Coincard />
),如果我使用PureComponent,它将不会无休止地登录render () {
,但是在componentWillRecieveProps
中,如果我执行console.log(nextProps)
,我可以一次又一次地看到相同的日志
答案 0 :(得分:2)
在代码中有一些要注意的地方。
PureComponent
,如果props
等于浅则不会重新呈现。Flatlist
组件内部渲染ScrollView
,这会使该组件一次渲染其内部的所有组件,这可能会导致Flatlist
和{{1 }}。ScrollView
,以减少为其他道具渲染组件的次数。要注意的另一件事是,根据以下提到的log语句,仅组件中的道具会在滚动底部呈现。
height
此额外的日志记录是由于道具从import {Dimensions} from 'react-native'
const {width, height} = Dimensions.get('window)
class CoinCard extends React.PureComponent {
render () {
console.log(this.props.item.long) //... Check the prop changes here, pass the item prop in parent Flatlist. This logs component prop changes which will show that same items are not being re-rendered but new items are being called.
return (
<View style={{height / 10, width}}> //... Render 10 items on the screen
<Text>
Text
</Text>
</View>
)
}
}
到您的组件而没有 PureComponent浅层比较。
请注意,不建议使用 componentWillReceiveProps(),并且应在代码中避免使用它们。
Flatlist
在后台运行,并使用React.PureComponent
在shouldComponentUpdate
和current
道具之间进行浅层比较。因此,在您的 PureComponent'中登录updated
会记录唯一列表,该列表可以检查。
答案 1 :(得分:1)
在上面的评论中,雅各布似乎设法使该组件仅呈现两次。
这肯定会导致两次初始渲染(如果不是PureComponent,则会导致无限渲染):
componentDidUpdate() {
var updateCoinData;
if (!updateCoinData) { // <- this is always true
updateCoinData = [...this.props.cryptoLoaded];
this.setState({updateCoinData: true}); // <- this will trigger a re render since `this.state.updateCoinData` is not initially true
}
...
}
答案 2 :(得分:1)
就像izb提到的那样,pb的根本原因是在纯组件上进行的业务调用,而该组件只是在加载时进行的。这是因为您的组件做出了业务决策(<=>“我决定何时必须在自己体内显示某些内容”)。这不是React的好习惯,甚至在使用redux时也不是。该组件必须尽可能地愚蠢,甚至不能决定做什么和何时进行。
我在您的项目中看到,您没有正确处理组件和容器的概念。您的容器中不应包含任何逻辑,因为它应该只是愚蠢的纯组件的包装。像这样:
import { connect, Dispatch } from "react-redux";
import { push, RouterAction, RouterState } from "react-router-redux";
import ApplicationBarComponent from "../components/ApplicationBar";
export function mapStateToProps({ routing }: { routing: RouterState }) {
return routing;
}
export function mapDispatchToProps(dispatch: Dispatch<RouterAction>) {
return {
navigate: (payload: string) => dispatch(push(payload)),
};
}
const tmp = connect(mapStateToProps, mapDispatchToProps);
export default tmp(ApplicationBarComponent);
和匹配的组件:
import AppBar from '@material-ui/core/AppBar';
import IconButton from '@material-ui/core/IconButton';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import { StyleRules, Theme, withStyles, WithStyles } from '@material-ui/core/styles';
import Tab from '@material-ui/core/Tab';
import Tabs from '@material-ui/core/Tabs';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import AccountCircle from '@material-ui/icons/AccountCircle';
import MenuIcon from '@material-ui/icons/Menu';
import autobind from "autobind-decorator";
import * as React from "react";
import { push, RouterState } from "react-router-redux";
const styles = (theme: Theme): StyleRules => ({
flex: {
flex: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
root: {
backgroundColor: theme.palette.background.paper,
flexGrow: 1
},
});
export interface IProps extends RouterState, WithStyles {
navigate: typeof push;
}
@autobind
class ApplicationBar extends React.PureComponent<IProps, { anchorEl: HTMLInputElement | undefined }> {
constructor(props: any) {
super(props);
this.state = { anchorEl: undefined };
}
public render() {
const auth = true;
const { classes } = this.props;
const menuOpened = !!this.state.anchorEl;
return (
<div className={classes.root}>
<AppBar position="fixed" color="primary">
<Toolbar>
<IconButton className={classes.menuButton} color="inherit" aria-label="Menu">
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" className={classes.flex}>
Title
</Typography>
<Tabs value={this.getPathName()} onChange={this.handleNavigate} >
{/* <Tabs value="/"> */}
<Tab label="Counter 1" value="/counter1" />
<Tab label="Counter 2" value="/counter2" />
<Tab label="Register" value="/register" />
<Tab label="Forecast" value="/forecast" />
</Tabs>
{auth && (
<div>
<IconButton
aria-owns={menuOpened ? 'menu-appbar' : undefined}
aria-haspopup="true"
onClick={this.handleMenu}
color="inherit"
>
<AccountCircle />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={this.state.anchorEl}
anchorOrigin={{
horizontal: 'right',
vertical: 'top',
}}
transformOrigin={{
horizontal: 'right',
vertical: 'top',
}}
open={menuOpened}
onClose={this.handleClose}
>
<MenuItem onClick={this.handleClose}>Profile</MenuItem>
<MenuItem onClick={this.handleClose}>My account</MenuItem>
</Menu>
</div>
)}
</Toolbar>
</AppBar>
</div >
);
}
private getPathName(): string {
if (!this.props.location) {
return "/counter1";
}
return (this.props.location as { pathname: string }).pathname;
}
private handleNavigate(event: React.ChangeEvent<{}>, value: any) {
this.props.navigate(value as string);
}
private handleMenu(event: React.MouseEvent<HTMLInputElement>) {
this.setState({ anchorEl: event.currentTarget });
}
private handleClose() {
this.setState({ anchorEl: undefined });
}
}
export default withStyles(styles)(ApplicationBar);
然后,您会告诉我:“但是我在哪里发起可以填满列表的呼叫?” 好吧,我在这里看到您使用redux-thunk(我更喜欢redux可观察的...学习起来比较复杂,但是waaaaaaaaaaaaaaaaaaay更强大),然后应该将其重做,从而启动此调度!
总结:
如果您遵循这些原则,那么您永远都不会遇到任何问题,例如“为什么要两次被打扰?以及……是谁打出来的?以及为什么此时此刻?”
其他方面:如果您使用redux,请使用不变性框架。否则,您可能会遇到问题,因为reducer必须是纯函数。为此,您可以使用一种流行的immutable.js,但一点都不方便。后来的驱逐者实际上是一个杀手er:沉浸(作者或暴民制造)。