We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React 组件非常的强大和灵活,提供了强大的功能,但是随着时间的推移,它将变的越来越臃肿和繁琐。
React
与任何其他类型的编程相比,React 组件遵循单一功能原则( single responsibility principle),它不仅使你的组件更容易维护,更重要的是复用性。然而,如何负责一个庞大的React组件不再是一件容易的事。下面从易到难我买将有有三种方法具体讲解这个问题。
render()
这个是最常见的方法:当一个组件要呈现太多的元素,把这些元素分解为逻辑子组件是一个简单的简化方法。
一个常见而快速的方法就是在同一类中拆分render()方法并创建额外的“sub-render”方法:
“sub-render”
class Panel extends React.Component { renderHeading() { // ... } renderBody() { // ... } render() { return ( <div> {this.renderHeading()} {this.renderBody()} </div> ); } }
虽然这中方法有它的用处,但这不是一个真正的拆分组件本身的方法。每个state、props和class methods等仍然是共享的,所以很难确定哪些是由每个sub-render所使用。
state
props
class methods
sub-render
要真正降低复杂性,应该创建全新的组件。对于更简单的子部件,功能组件可用于将样板保持在最低限度:
const PanelHeader = (props) => ( // ... ); const PanelBody = (props) => ( // ... ); class Panel extends React.Component { render() { return ( <div> // Nice and explicit about which props are used <PanelHeader title={this.props.title}/> <PanelBody content={this.props.content}/> </div> ); } }
通过这种方式拆分还有一个微妙但重要的区别。代替直接与间接函数调用组件声明,我们以更小的单位来组织React代码。这是因为返回的‘Panel’的render()是一个树形的元素,只有走到PanelHeader 和 PanelBody,而不是在它们之下的所有元素。
PanelHeader
PanelBody
来看个实际意义的测试:一个浅渲染可以用来轻松隔离这些单位进行独立的测试。作为奖励,当React的新语境体系到达时,规模较小的单位将使它更有效地执行增量呈现。
当一个组件由于多个变量或者配置变得非常的复杂,考虑将这个组件转化成一个“模版”组件。这就需要一个专用的父组件集中配置它们。
例如,一个Comment组件可能有不同的actions或者 metadata显示,取决于你是不是作者,是否保存成功,或者有无权限等。而不是混合的Comment组件,在哪里以及如何呈现它的内容?用逻辑来处理所有的变化?这两个问题可以被认为是独立的。使用React通过元素的能力,不仅仅用数据作为props,来创建一个灵活的模板组件。
Comment
actions
metadata
class CommentTemplate extends React.Component { static propTypes = { // Declare slots as type node metadata: PropTypes.node, actions: PropTypes.node, }; render() { return ( <div> <CommentHeading> <Avatar user={...}/> // Slot for metadata <span>{this.props.metadata}</span> </CommentHeading> <CommentBody/> <CommentFooter> <Timestamp time={...}/> // Slot for actions <span>{this.props.actions}</span> </CommentFooter> </div> ); } }
另一个组件可以找出的独有的metadata和actions。
class Comment extends React.Component { render() { const metadata = this.props.publishTime ? <PublishTime time={this.props.publishTime} /> : <span>Saving...</span>; const actions = []; if (this.props.isSignedIn) { actions.push(<LikeAction />); actions.push(<ReplyAction />); } if (this.props.isAuthor) { actions.push(<DeleteAction />); } return <CommentTemplate metadata={metadata} actions={actions} />; } }
请记住,在JSX中,组件的开始和结束标签之间的任何东西都作为特殊的子类支持传递。当正确使用时,可以特别表现出来。为了使用习惯,应该作为组件的主要内容区域保留。在评论示例中,这可能是注释本身的文本。
JSX
<CommentTemplate metadata={metadata} actions={actions}> {text} </CommentTemplate>
组件常常会被与其主要并且直接相关的交叉问题所污染。
假设你只要点击Document组件中的一个链接来发送分析数据。为了使事情进一步复杂化,数据需要包括有关文档的信息,如ID。简单的解决方案可能是向Document的componentDidMount和componentWillUnmount生命周期方法添加代码,如下所示:
Document
componentDidMount
componentWillUnmount
class Document extends React.Component { componentDidMount() { ReactDOM.findDOMNode(this).addEventListener('click', this.onClick); } componentWillUnmount() { ReactDOM.findDOMNode(this).removeEventListener('click', this.onClick); } onClick = (e) => { if (e.target.tagName === 'A') { // Naive check for <a> elements sendAnalytics('link clicked', { documentId: this.props.documentId // Specific information to be sent }); } }; render() { // ... } }
这里有一些问题:
可以使用高阶分量(HOCs)来分解这样的问题。简而言之,这些函数可以应用于任何React组件,用所需的行为来包装该组件。
function withLinkAnalytics(mapPropsToData, WrappedComponent) { class LinkAnalyticsWrapper extends React.Component { componentDidMount() { ReactDOM.findDOMNode(this).addEventListener('click', this.onClick); } componentWillUnmount() { ReactDOM.findDOMNode(this).removeEventListener('click', this.onClick); } onClick = (e) => { if (e.target.tagName === 'A') { // Naive check for <a> elements const data = mapPropsToData ? mapPropsToData(this.props) : {}; sendAnalytics('link clicked', data); } }; render() { // Simply render the WrappedComponent with all props return <WrappedComponent {...this.props} />; } } return LinkAnalyticsWrapper; }
重要的是要注意,该函数不会使组件变异以添加其行为,而是返回一个新的包装组件。正是这个新的包装组件用于代替原始的Document组件。
class Document extends React.Component { render() { // ... } } export default withLinkAnalytics((props) => ({ documentId: props.documentId }), Document);
请注意,可以提取要发送的数据(documentId)的具体细节作为HOCs的配置。这可以保留文档的领域知识与文档组件,以及通用的方式来监听withLinkAnalytics HOCs中的点击。
documentId
withLinkAnalytics
高阶组件显示了React的强大的组合性质。这里的简单示例演示了如何将似乎紧密集成的代码提取到具有单一职责的模块中。
HOCs通常用于React库,例如react-redux,styled-components和react-intl。毕竟,这些库都是解决任何React应用程序的通用方面。另一个库——recompose,通过使用HOCs进一步管理从组件状态到生命周期方法的一切。
react-redux
styled-components
react-intl
recompose
React组件是高度可组合的设计。通过轻松分解和组合它们,可以利用这一点。
不要躲避创建小而重要的组件。起初它可能会让你感到尴尬,但结果将是更强大和可重用的代码。
原文:https://medium.com/dailyjs/techniques-for-decomposing-react-components-e8a1081ef5da
The text was updated successfully, but these errors were encountered:
No branches or pull requests
React
组件非常的强大和灵活,提供了强大的功能,但是随着时间的推移,它将变的越来越臃肿和繁琐。与任何其他类型的编程相比,
React
组件遵循单一功能原则( single responsibility principle),它不仅使你的组件更容易维护,更重要的是复用性。然而,如何负责一个庞大的React
组件不再是一件容易的事。下面从易到难我买将有有三种方法具体讲解这个问题。1. 分离
render()
方法这个是最常见的方法:当一个组件要呈现太多的元素,把这些元素分解为逻辑子组件是一个简单的简化方法。
一个常见而快速的方法就是在同一类中拆分
render()
方法并创建额外的“sub-render”
方法:虽然这中方法有它的用处,但这不是一个真正的拆分组件本身的方法。每个
state
、props
和class methods
等仍然是共享的,所以很难确定哪些是由每个sub-render
所使用。要真正降低复杂性,应该创建全新的组件。对于更简单的子部件,功能组件可用于将样板保持在最低限度:
通过这种方式拆分还有一个微妙但重要的区别。代替直接与间接函数调用组件声明,我们以更小的单位来组织
React
代码。这是因为返回的‘Panel’的render()
是一个树形的元素,只有走到PanelHeader
和PanelBody
,而不是在它们之下的所有元素。来看个实际意义的测试:一个浅渲染可以用来轻松隔离这些单位进行独立的测试。作为奖励,当
React
的新语境体系到达时,规模较小的单位将使它更有效地执行增量呈现。模版组件通过
React
元素作为props
当一个组件由于多个变量或者配置变得非常的复杂,考虑将这个组件转化成一个“模版”组件。这就需要一个专用的父组件集中配置它们。
例如,一个
Comment
组件可能有不同的actions
或者metadata
显示,取决于你是不是作者,是否保存成功,或者有无权限等。而不是混合的Comment
组件,在哪里以及如何呈现它的内容?用逻辑来处理所有的变化?这两个问题可以被认为是独立的。使用React
通过元素的能力,不仅仅用数据作为props
,来创建一个灵活的模板组件。另一个组件可以找出的独有的
metadata
和actions
。请记住,在
JSX
中,组件的开始和结束标签之间的任何东西都作为特殊的子类支持传递。当正确使用时,可以特别表现出来。为了使用习惯,应该作为组件的主要内容区域保留。在评论示例中,这可能是注释本身的文本。将常见方面提取到高阶组件中
组件常常会被与其主要并且直接相关的交叉问题所污染。
假设你只要点击
Document
组件中的一个链接来发送分析数据。为了使事情进一步复杂化,数据需要包括有关文档的信息,如ID。简单的解决方案可能是向Document
的componentDidMount
和componentWillUnmount
生命周期方法添加代码,如下所示:这里有一些问题:
可以使用高阶分量(HOCs)来分解这样的问题。简而言之,这些函数可以应用于任何
React
组件,用所需的行为来包装该组件。重要的是要注意,该函数不会使组件变异以添加其行为,而是返回一个新的包装组件。正是这个新的包装组件用于代替原始的
Document
组件。请注意,可以提取要发送的数据(
documentId
)的具体细节作为HOCs的配置。这可以保留文档的领域知识与文档组件,以及通用的方式来监听withLinkAnalytics
HOCs中的点击。高阶组件显示了
React
的强大的组合性质。这里的简单示例演示了如何将似乎紧密集成的代码提取到具有单一职责的模块中。HOCs通常用于
React
库,例如react-redux
,styled-components
和react-intl
。毕竟,这些库都是解决任何React
应用程序的通用方面。另一个库——recompose
,通过使用HOCs进一步管理从组件状态到生命周期方法的一切。结论
React
组件是高度可组合的设计。通过轻松分解和组合它们,可以利用这一点。不要躲避创建小而重要的组件。起初它可能会让你感到尴尬,但结果将是更强大和可重用的代码。
原文:https://medium.com/dailyjs/techniques-for-decomposing-react-components-e8a1081ef5da
The text was updated successfully, but these errors were encountered: