我正在准备为一场聚会做技术分享,我想花点时间和大家分享一下我在 React 组件模式上学到的东西。Components(组件) 是 React 的核心,因此,了解如何利用它们对于构建优秀的设计结构至关重要。
所提供的示例受到了 的极大启发。我强烈建议观看视频。
什么是组件
根据 ,“组件(Components) 让你可以将用户界面分成独立的,可复用的小部件,并可以对每个部件进行单独的设计。”
当你第一次运行 npm install react
时,你得到了一个东西:组件及其API。与 JavaScript 函数类似,组件接受名为 “props” 的输入并返回 React 元素,它描述(声明)用户界面(UI)应该是什么样子。这就是为什么 React 被称为声明式 API 的原因,因为你告诉它你想要的 UI 看起来长什么样,剩下的事情 React 会帮你处理。
可以将声明式想象成你坐出租车去某个地方,你只需告诉司机你想去哪儿,然后他/她会实际驾驶到达目的地。而命令式本赛季恰好相反,你要亲自开车才能到达目的地。
组件 API
那么当你安装 React 时,得到的API到底是什么呢?有五个,他们是:
- render
- state
- props
- context
- lifecycle events
虽然每个组件具有充分利用上述所有 API 的能力,但你会发现一些组件会使用部分 API ,而其他组件仅使用另一些 API 。这种情况可以将组件们划分为两类,称为 有状态(stateful) 组件 和 无状态(stateless) 组件 。有状态组件通常使用 有状态的(stateful) API:如render, state 和 lifecycle events,而无状态组件则使用 render, props 和 context。
现在我们要开始介绍组件模式了。组件模式是最佳的使用实践,并首先被引入来分割 数据或逻辑层 和 UI或表示层 。通过划分组件之间的职责,你可以创造更多可重复使用的,结合更加紧密的组件,以用来构建复杂的UI。在构建可扩展的应用程序时,这一点尤为重要。
组件模式
常见的组件模式是:
- 容器(Container)组件
- 展示(Presentational)组件
- 高阶组件(Higher order components , HOC )
- 渲染回调(Render callback)
容器(Container)组件
“容器组件就是执行数据提取,然后渲染其子组件而已” —— Jason Bonta
图注:蓝色表示容器(Container)组件,而灰色表示展示(Presentational)组件
容器组件是您的数据或逻辑层,并利用 有状态的(stateful) API 。使用生命周期事件,您可以连接到 Redux 或 Flux 等状态管理管理容器,并将数据和回调作为 props(属性) 传递给子组件。在容器组件的 render 方法中,您可以将展示子组件组合成 UI 。为了能够访问所有 有状态的(stateful) API,容器组件必须是 类(class)组件 而不是函数式组件。
在下面的示例中,我们有一个名为 Greeting
的类组件,它具有 state(状态),生命周期事件 componentDidMount()
和 render
。
class Greeting extends React.Component { constructor() { super(); this.state = { name: "", }; } componentDidMount() { // AJAX this.setState(() => { return { name: "William", }; }); } render() { return ( <div> <h1>Hello! {this.state.name}</h1> </div> ); } }
此时,此组件是 有状态的(stateful) 类组件。为了使 Greeting
成为容器组件,我们可以将UI拆分为展示组件,我将在下面说明。
展示组件
展示(Presentational)组件 使用 props(属性) , render 和 context (无状态(stateless) API),并且可以是语法简单的函数式无状态组件。
const GreetingCard = (props) => { return ( <div> <h1>Hello! {props.name}</h1> </div> ) }
展示组件仅从 props(属性) 接收数据和回调,这些 props(属性) 可以由其容器组件或父组件提供。
图注:蓝色是展示组件,灰色是容器组件
将容器组件的逻辑 和 展示组件的表现 封装在各自的组件中,结合在一起:
const GreetingCard = (props) => { return ( <div> <h1>{props.name}</h1> </div> ) } class Greeting extends React.Component { constructor() { super(); this.state = { name: "", }; } componentDidMount() { // AJAX this.setState(() => { return { name: "William", }; }); } render() { return ( <div> <GreetingCard name={this.state.name} /> </div> ); } }
如您所见,我已将 Greeting
类组件中的表示部分移除到其 函数式无状态的组件(即 GreetingCard
展示组件) 中。当然,这是一个非常简单的例子 – 对于更复杂的应用程序,这是至关重要的。
高阶组件(Higher order components , HOC )
高阶组件是一个将组件作为参数并返回新组件的函数。
这是一种功能强大的模式,用来为任意数量的组件提供数据,并可用于重用组件逻辑。 想想 react-router-v4 和 Redux 。 使用react-router-v4,您可以使用 withRouter()
继承作为 props 传递给组件的方法。 使用 Redux ,您可以访问在connect({})()
时作为 props 传递的操作。
图注:高阶组件用虚线表示,它是一种返回组件的函数
来看个示例:
import {withRouter} from 'react-router-dom'; class App extends React.Component { constructor() { super(); this.state = {path: ''} } componentDidMount() { let pathName = this.props.location.pathname; this.setState(() => { return { path: pathName, } }) } render() { return ( <div> <h1>Hi! I'm being rendered at: {this.state.path}</h1> </div> ) } } export default withRouter(App);
导出我的组件时,我用 react-router-v4 的 withRouter() 包裹它。在 App
的生命周期事件 componentDidMount()
中,我正在使用 this.props.location.pathname
提供的值更新 state(状态) 。现在,我的类组件 通过 withRouter()
包装后可以通过 props 访问 react-router-v4 的方法,因此 this.props.location.pathname
。这只是众多例子中的其中一个。
更多关于 说明请查看官方文档
渲染回调(Render callback)
注:这种模式以前也叫 函数作为子组件(Function as Child Components) , 现在最新的叫法为:渲染属性(Render Props),请参阅。
与高阶组件类似,渲染回调或渲染 props 用于共享或重用组件逻辑。
虽然许多开发人员更多地倾向于使用 HOC 的可重用逻辑,但使用渲染回调有一些非常好的理由和优势 – Michael Jackson的 “ ” 中得到了最好的解释。简而言之,渲染回调为我们难能可贵地减少了命名空间冲突,并更好的说明了逻辑来源。
图注:蓝色虚线表示渲染回调
class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0, }; } increment = () => { this.setState(prevState => { return { count: prevState.count + 1, }; }); }; render() { return ( <div onClick={this.increment}>{this.props.children(this.state)}</div> ); } } class App extends React.Component { render() { return ( <Counter> {state => ( <div> <h1>The count is: {state.count}</h1> </div> )} </Counter> ); } }
在 Counter
类中,我在 render 方法中嵌入 this.props.children
并将 this.state
作为参数。 在 App
类中,我能够将我的组件包装在 Counter
组件中,因此可以访问 Counter
的逻辑。 渲染回调部分是第28行,{state => ()}
– bam! 我可以自动访问上面的 Counter
的 state(状态) 了。
更多关于 说明请查看官方文档
谢谢你的阅读!
我总是乐于接受任何建议,以便更好地解释 – 我通过写作来学习,所以这远远不够完美,这只是我对 React 组件模式的看法。
原文链接:
最新评论
写的挺好的
有没有兴趣翻译 impatient js? https://exploringjs.com/impatient-js/index.html
Flexbox playground is so great!
感谢总结。
awesome!
这个好像很早就看到类似的文章了
比其他的教程好太多了
柯理化讲的好模糊…没懂