HOC 传递属性

  • 本文关键字:属性 HOC reactjs
  • 更新时间 :
  • 英文 :


我最近在为复杂的 HOC 以及我如何只通过其中定义的新道具而不通过任何其他道具而苦苦挣扎。 更准确地说,假设我的 HOC 使用其他 HOC 来扩展其属性,例如

const withSession = (WrappedComponent) => {
class SessionProvider extends React.PureComponent {
constructor(props) {
super(props);
this.login = this.login.bind(this);
}
login() {
console.log('login'); // this will dispatch some action
// this.props.dispatch... 
}
render() {
return (
<WrappedComponent
doLogin={this.login}
{...this.props} 
/>
);
}
}
const mapStateToProps = null;
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
return compose(
withConnect,
withRouter,
)(injectIntl(SessionProvider));
};

在这里,SessionProvider利用dispatchinjectIntl将属性附加到其props。但是,我不想将这些道具传递给包装的组件。这个想法是有一个SessionProviderHOC,它具有一些 API 调用,但仅使用login扩展包装的组件。 我注意到,如果保持{...this.props},包装的组件也将获得我不想通过的 HOC 使用的所有props。 因此,我想通过更改 HOC 渲染方法来分解this.props来显式定义要传递的属性:

render() {
const { dispatch, intl, ...otherProps } = this.props;
return <WrappedComponent doLogin={this.login} { ...otherProps} />;
}

然而,如果包装组件本身有dispachintl道具,则这些道具不会通过 HOC。

我正在做的事情有什么问题吗?有什么更好的方法吗?我错过了什么吗?

你正在做的事情没有错。道具名称冲突是使用 HOC 时的已知问题。因此,据我所知,您可以使用的最佳替代方案是渲染道具模式,它有助于使组件尽可能render声明性。对于您的情况,请考虑如下内容:

class Session extends React.PureComponent {
constructor(props) {
super(props);
this.login = this.login.bind(this);
}
login() {
console.log("login"); // this will dispatch some action
// this.props.dispatch...
}
// ...
render() {
return (
<React.Fragment>
{this.props.children({
doLogin: this.login
doLogout: this.logout
// ...
})}
</React.Fragment>
);
}
}
// ...
return compose(
withConnect,
withRouter
)(injectIntl(Session));

并从另一个组件中使用它:

// ...
render() {
return (
<Session>
{({ doLogin, doLogout }) => (
<React.Fragment>
<SomeComponent doLogin={doLogin} />
<button onClick={doLogout}>Logout</button>
</React.Fragment>
)}
</Session>
)
}

更新:

在 v16.7.0-alpha中有一个非常有前途的Hooks 提案。我还不太熟悉它们,但它们倾向于更有效地解决组件的可重用性。

您需要复制静态属性,为此我使用以下代码.. 您可以根据需要添加更多属性

export const REACT_STATICS = {
childContextTypes: true,
contextTypes: true,
defaultProps: true,
displayName: true,
getDefaultProps: true,
mixins: true,
propTypes: true,
type: true
};
export const KNOWN_STATICS = {
name: true,
length: true,
prototype: true,
caller: true,
arguments: true,
arity: true
};
export function hoistStatics(targetComponent, sourceComponent) {
var keys = Object.getOwnPropertyNames(sourceComponent);
for (var i = 0; i < keys.length; ++i) {
const key = keys[i];
if (!REACT_STATICS[key] && !KNOWN_STATICS[key]) {
try {
targetComponent[key] = sourceComponent[key];
} catch (error) {}
}
}
return targetComponent;
}

// in HOC
const hoistedSessionProvider = hoistStatics(SessionProvider, WrappedComponent);
// use hoistedSessionProvider in compose

最新更新