[英]React Multiple Higher-Order Components
我刚刚发现在我的 React 项目中使用 HOC 的惊人好处。
我的问题是在一个组件上调用多个 HOC 函数会影响性能吗?
示例
export default withState(withLabel(withTheme(MyComponent)))
这当然只会render
一个组件,但是查看我的 React 开发工具,我可以看到输出的 HOC 组件深度为三层。 这是需要警惕的,还是有更好的方法来在一个组件上调用多个 HOC?
你的语法相当于做:
<StateProvider>
<LabelProvider>
<ThemeProvider>
<MyComponent />
</ThemeProvider>
</LabelProvider>
</StateProvider>
性能影响将来自这些 HOC 的实现方式。 您可能必须查看它们中的每一个。
示例:
总之,没有硬性规定。 您的主要重点应该是了解这些 HOC 的作用,并尝试限制对您的应用程序进行不必要的重新渲染。
这个答案并不完全是关于性能的 ,但是当我必须一次使用一些高阶组件来包装相同的组件时,我做的一件事就是创建一个主要的高阶组件,它将调用我需要的所有高阶组件。我可以用这个换一下。
创建一个主要的高阶组件使它们更容易重用,并且包装的组件更加整洁和易于阅读。
看看这个例子:
调用所有常见的“主”高阶组件:
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { withStyles, createStyles, Theme, WithStyles } from '@material-ui/core/styles';
import withSnack, { Snack } from '../CommonComponents/SnackBar/WithSnack';
import withCancelable, { Cancelable } from '../CommonComponents/WithCancelable';
interface Props {
snack: Snack;
cancelable: Cancelable;
history: any;
};
type WithCommon =
(TargetComponent: React.ComponentClass, componentsToInclude?: string[], styles?: any, mapStateToProps?: any, mapDispatchToProps?: any) => React.ComponentClass<Props, any>;
const withCommon = (
TargetComponent: any,
componentsToInclude: string[] | undefined = undefined,
styles: any | undefined = undefined,
mapStateToProps: any | undefined = undefined,
mapDispatchToProps: any | undefined = undefined
): WithCommon => {
const withCommonComponent = (props: Props) => (<TargetComponent {...props} />)
let resultComponent: any = withCommonComponent;
if (mapStateToProps || mapDispatchToProps) {
resultComponent = connect(
mapStateToProps,
mapDispatchToProps
)(withCommonComponent);
}
if (styles) {
resultComponent = withStyles(styles)(resultComponent);
}
if (componentsToInclude) { // "extra" components to wrap the target
if (componentsToInclude.length) {
if (componentsToInclude.some(c => c === 'router')) {
resultComponent = withRouter(resultComponent);
}
if (componentsToInclude.some(c => c === 'snack')) {
resultComponent = withSnack(resultComponent);
}
if (componentsToInclude.some(c => c === 'cancelable')) {
resultComponent = withCancelable(resultComponent);
}
} else { // if array is empty then include them all (avoids typing the same ones every time you need to have them all)
resultComponent = withCancelable(withSnack(withRouter(resultComponent)));
}
}
return resultComponent;
};
export interface Common extends WithStyles {
snack: Snack,
cancelable: Cancelable,
history: any,
};
export default withCommon;
然后由上面的主要组件包裹的组件:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Avatar from '@material-ui/core/Avatar';
import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import Dialog from '@material-ui/core/Dialog';
import { createStyles, Theme } from '@material-ui/core/styles';
import TextFieldOutlined from '../CommonComponents/Form/TextFieldOutlined';
import { hideSignin } from '../Redux/ActionCreators'
import Auth from '../Auth/Auth';
import Form from '../CommonComponents/Form/Form';
import Loading from '../CommonComponents/Loading';
import withCommon, { Common } from '../CommonComponents/WithCommon';
const styles = (theme: Theme) =>
createStyles({...});
interface Props extends Common {
show: boolean;
hideSignin(): void;
};
interface State {...}
class SignIn extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.handleEmailchange = this.handleEmailchange.bind(this);
this.handlePasswordchange = this.handlePasswordchange.bind(this);
this.signin = this.signin.bind(this);
}
signin(e: any): void {
if (!e.currentTarget.form.reportValidity()) {
return;
}
e.preventDefault();
this.setState({ loading: true });
Auth.signin(
this.state.email,
this.state.password,
(promise: Promise<any>) => this.props.cancelable.setCancelable('signin', promise),
)
.then(() => {
this.props.history.push('/');
})
.catch((err: any) => {
this.props.snack.error(err.message);
})
.finally(() => {
this.setState({ loading: false });
});
}
render() {
const { classes } = this.props;
return (
<Dialog
open={this.props.show}
onClose={() => this.props.hideSignin()}
>
<Paper className={classes.paper}>
<Loading loading={this.state.loading} />
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5" className={classes.label}>
Sign in
</Typography>
<Form submitButtonText="Sign in" onSubmit={this.signin}>
<TextFieldOutlined required label="Email Address" type="email" onChange={this.handleEmailchange} focused />
<TextFieldOutlined required label="Password" type="password" onChange={this.handlePasswordchange} />
</Form>
</Paper>
</Dialog>
);
}
}
(SignIn as React.ComponentClass<Props, State>).propTypes = {
classes: PropTypes.object.isRequired,
} as any;
const mapStateToProps = (state: any) => {
return {
show: state.signin.show,
};
}
const mapDispatchToProps = (dispatch: any) => {
return {
hideSignin: () => dispatch(hideSignin()),
}
}
// Empty array will make it include all the "extra" higher order components
export default withCommon(SignIn, [], styles, mapStateToProps, mapDispatchToProps) as any;
// Or you could specify which extra components you want to use:
// export default withCommon(SignIn, ['router', 'cancelable'], styles, mapStateToProps, mapDispatchToProps) as any;
// Or some nulls if you just want to connect the store:
// export default withCommon(SignIn, null, null, mapStateToProps, mapDispatchToProps) as any;
我不会用那个。 当您查看MyComponent
组件时,理解 props 的来源很复杂。 使用这种模式还有更多的缺点。 无论如何,如果您决定使用HOC
以正确的方式使用它,例如
const withDetails = Component => {
const C = props => {
// do something
}
// assign display & wrapped names - easier to debug
C.displayName = `withRouter(${Component.displayName))`
C.WrappedComponent = Component;
return C;
}
建议查看render props
反应模式,而不是使用HOC
si。 Use a Render Prop!
进行了很好的解释Use a Render Prop!
Michael Jackson(反应路由器创建者)的文章。
希望这是有道理的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.