我最近在为复杂的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
使用dispatch
和injectIntl
将属性附加到其props
。但是,我不想将这些道具传递给包装的组件。这个想法是要有一个SessionProvider
HOC,它具有一些API调用,但仅使用login
扩展了包装的组件。
我注意到,如果保留{...this.props}
,则被包装的组件还将获得HOC所使用的所有props
,我不想通过。
因此,我想通过更改HOC渲染方法来分解this.props
来明确定义要传递的属性:
render() {
const { dispatch, intl, ...otherProps } = this.props;
return <WrappedComponent doLogin={this.login} { ...otherProps} />;
}
但是,如果WrappedComponent本身具有dispach
或intl
道具,则这些道具不会通过HOC传递。
我的工作有什么问题吗?有更好的方法吗?我想念什么吗?
答案 0 :(得分:1)
您所做的没有错。使用HOC时,道具名称冲突是一个已知问题。因此,据我所知,您可以使用的最佳替代方法是 Render Props 模式,该模式有助于使组件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 Proposal 。我对它们还不太熟悉,但是它们往往可以更有效地解决组件的可重用性。
答案 1 :(得分:0)
您需要复制静态属性,为此我在下面的代码中使用..您可以根据需要添加更多属性
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