就像 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 observable……学习起来更复杂,但 waaaaaaaaaaaaaaaaay 更强大),那么这应该是启动调度的 thunk!
总结一下:
- 组件:最愚蠢的元素,通常应该只有渲染方法,以及一些其他的方法处理程序来冒泡用户事件。此方法只负责向用户显示其属性。除非您拥有仅属于该组件的视觉信息(例如弹出窗口的可见性),否则不要使用状态。任何显示或更新的内容都来自上面:更高级别的组件或容器。它不会决定更新自己的值。充其量,它处理一个子组件上的用户事件,然后在上面冒泡出另一个事件,并且......也许在某个时候,它的容器会返回一些新的属性!
- 容器:非常愚蠢的逻辑,包括将顶级组件包装到 redux 中,以便将事件插入到操作中,并将存储的某些部分插入到属性中
- Redux thunk(或 redux observable):它处理整个用户应用程序逻辑。这个人是唯一知道什么时候触发什么的人。如果前端的一部分必须包含复杂性,那就是这个!
- Reducers:定义如何组织存储中的数据以使其尽可能易于使用。
- 存储:理想情况下,每个顶级容器一个,唯一一个包含必须向用户显示的数据。其他人不应该。
如果您遵循这些原则,您将永远不会遇到诸如“为什么这被称为两次?以及……是谁制造的?为什么在此时此刻?”之类的问题。
别的东西:如果你使用 redux,使用不变性框架。否则你可能会遇到问题,因为 reducer 必须是纯函数。为此,您可以使用流行的 immutable.js 但一点也不方便。而后来的 ousider 实际上是一个杀手:immer(由作者或 mobx 制作)。