在 react 中 ,这是 React 的核心理念之一。
一个 React 组件就是一个 JavaScript 函数,并且你可以在函数中书写 markup——主要指的是 HTML 等 “标记语言” 代码。React 的 markup 类似 Vue 的 vnode 对象。
React 组件官方推荐采用 JSX 语法,具体请看:在 React 中使用 JSX。
React 内置组件的使用格式:。
React 内置组件包括:
- Component 组件:通过继承该组件可以创建一个 class 组件。
- Smart 组件:
- Smart 组件又称为 容器组件,它负责处理应用的数据以及业务逻辑,同时将状态数据与操作函数作为属性传递给子组件;
- 一般而言它仅维护很少的 DOM,其所有的 DOM 也仅是作为布局等作用。
- Dumb 组件:
- Dumb 组件又称为 木偶组件,它负责展示作用以及响应用户交互,它一般是无状态的(在如 Modal 等类组件中可能会维护少量自身状态);
- 一般而言 Dumb 组件会拆分为一个个可复用、功能单一的组件。因此 Dumb 组件使用函数式组件定义,当其需要对重渲染进行优化时则可以使用 PureComponent 组件。
- PureComponent 组件:是对 Component 组件的性能优化。通过继承该组件可以创建一个 class 组件。
- Fragment 组件:
- 允许你将子列表分组,而无需向 DOM 添加额外节点。
- 在不需要设置 key 属性的前提下,该组件可以简写为
PureComponent 组件是基于 Component 组件实现的,是对 Component 组件的性能优化。
通过 extends PureComponent 组件能够创建一个 class 组件。
Component 组件 和 PureComponent 组件的差异:
- 在 Component 组件中
- 生命周期 shouldComponentUpdate 钩子函数返回 true 时才会进行重渲染,如果返回 false 则不会进行重渲染,默认总是返回 true。
- 有时我们需要在 shouldComponentUpdate 中进行逻辑判断,来自定义组件是否需要重渲染,以提升组件的性能。
- 在 PureComponent 组件中
- 自动通过 props 和 state 的浅对比来重写了 shouldComponentUpate 钩子——只有在 state 或 props 发生变化时 shouldComponentUpate 钩子才返回 true,默认是 false。
- 不需要开发者自己实现 shouldComponentUpdate,就可以进行简单的判断来提升性能。
建议在编写 class 组件时,直接继承 PureComponent 组件即可。
例如:
Fragments 组件允许你将子列表分组,而无需向 DOM 添加额外节点。
例如:
在不需要设置 key 属性的情况下, 组件可以简写为 :
当需要给 Fragments 标签添加 key 属性时,不能使用简短的“空标签”语法,必须显示的使用 React.Fragments:
- function 组件:
- 无需使用 render() 方法,直接访问 props 即可。
- class 组件:
- 使用 ES6 class 语法 extends React 的 PureComponent 或 Component 组件。
- 需要使用 render() 方法,在 render() 方法中要用 this 才能访问 props。
function 组件是创建 React 组件的最简单方式。
例如:
(1)、将 function 组件转换成 class 组件
通过以下 5 步将 function 组件转成 class 组件:
- 创建一个同名的 ES6 class,并且继承于 React.Component。
- 添加一个空的 render() 方法。
- 将函数体移动到 render() 方法之中。
- 在 render() 方法中使用 this.props 替换 props。
- 删除剩余的空函数声明。
React 的 class 组件使用的是 ES6 的 class 语法,通过 extends React 的 PureComponent 或 Component 组件来定义的。
例如:
(1)、super() 里边到底要不要传入 props 参数?
若显示声明了 constructor 方法,则必须要调用super;
在组件的非 constructor 方法中 使用 props 时,可不用传入,直接使用;
在 constructor 内使用 props ,则必须要将 props 传入 super 中。
以上说法,在ES7之后是无效的。(待研究)
(2)、类组件中到底要不要定义构造函数 constructor() ?
ES6 中新增了类的概念,一个类必须要有 constructor 方法,如果在类中没有显示定义,则一个空的 constructor 方法会被默认添加;
一般需要在构造函数中初始化state和绑定事件,因此当需要初始化state或绑定事件时,需要显示定义 constructor 方法,并在 constructor 方法中初始化 state 和绑定事件。
例如:
ES7+ 简化了上述写法,规定 class 中可以不实现 constructor 方法,state 这样定义(据说会有一些问题,请参阅此文):
(3)、绑定事件到底要不要在构造函数 constructor() 中进行?
一般需要在构造函数中绑定事件,但需要使用 bind,如果不想调用 bind,也可以使用箭头函数来定义函数(箭头函数不会改变 this 指向)。
举例说明:
运行上述代码是会报错的。解决办法有两种:
- bind 指定 this 指向;
- 箭头函数定义方法保证 this 指向不变。
①、bind 指定 this 指向
方式一:直接在 constructor 里使用 bind 绑定 this
但是这样又一个弊端:该方法会在页面第一次渲染时被执行一次。 可以跟“方式二”对比一下。
方式二:在 render 函数里调用方法时绑定 this
②、箭头函数定义方法保证 this 指向不变
方式一:直接在 render 函数外用箭头函数定义方法——使用 class fields 绑定回调函数
方式二:直接在 render 函数里用箭头函数定义并使用方法——在回调中使用箭头函数
【注意】若采用在回调中使用箭头函数,当回调函数作为一个属性值传入低阶组件,上述这种方法可能会进行额外的重新渲染。建议使用 class fields 绑定回调函数 来避免这类性能问题。
React 组件的生命周期
React 官方:组件状态
Props vs State
ReactJS: Props vs. State
props 对象和 state 对象都是用来保存信息的,这些信息可以控制组件的渲染输出。当 props 和 state 的任一个发生变化都会触发组件的渲染更新。我们只需要通过更新该组件的 state 和其子组件的 props 就能重新渲染用户界面,尽量避免直接操作真实 DOM。
props 对象和 state 对象的一个重要的不同点就是:
- props 是传递给组件的(类似于函数的形参)。React 组件不能改变自身的 props 对象,只可以父组件改变传递给子组件的 props 对象。
- state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)。React 组件可以改变自身的 state 对象。
因为 React 组件就是 JavaScript 函数,所以 props 对象可以理解为“函数的形参”。
React 里任何组件都不能修改自身的 props 对象——像这样的函数被称为“纯函数”——该函数不会尝试更改入参,且多次调用下相同的入参始终返回相同的结果。
(1)、使用 props 对象
(2)、为 props 设置默认值
通过组件类的 defaultProps 属性为 props 设置默认值。
(3)、props 的类型检查——PropTypes
你可以借助第三方的 Flow 或 TypeScript 等 JavaScript 扩展来对整个应用程序做类型检查,也可以直接使用 React 内置的 PropTypes 功能来做 props 的类型检查。这里只看 PropTypes。
React.PropTypes 在 React v15.5 版本后已经移到了 prop-types 库。
react 使用 PropTypes 进行类型检查
React 官方提供了 PropTypes 使用不同验证器的例子
React 支持使用 propTypes 对 props 进行验证。它可以保证我们的应用组件被正确使用。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。
首先要安装 prop-types 包:
然后使用 prop-types:
state 是私有的,并且完全受控于当前组件。
构造函数(constructor)是唯一可以给 this.state 赋值的地方。之后,不要直接修改 State 对象,而是应该使用 setState() 方法来更新 state 对象(具体请看下文的 React 组件的 API 部分)。
当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state 对象。
(1)、使用 state 对象
(2)、我应该如何更新那些依赖于当前的 state 的 state 呢?
给 setState 传递一个函数,而不是一个对象,就可以确保每次的调用都是使用最新版的 state。(具体请看下文的 React 组件的 API 部分)
【推荐文章】:
React 官方:组件状态
Props vs State
ReactJS: Props vs. State
React 组件 API
- setState:设置状态。
- replaceState:替换状态。
- setProps:设置属性。
- replaceProps:替换属性。
- forceUpdate:强制更新。
- findDOMNode:获取 DOM 节点(详见react 与 DOM)。
React 官方:组件状态
setState 方法是 React 事件处理函数 和 请求回调函数 中触发 UI 更新的主要方法。
setState 方法可以接收 2 个参数:
- nextState:将要设置的新状态,该状态会和当前的 state 合并。
- callback:可选参数,回调函数。该函数会在 setState 设置成功,且组件重新渲染后调用。
默认情况下,setState() 总是会触发一次组件重绘,除非在 shouldComponentUpdate() 钩子函数中实现了自定义一些条件渲染逻辑。
因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。否则可能会导致无法更新。要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。传递一个函数可以让你在函数内访问到当前最新的 state 的值。
例如:
replaceState 方法与 setState 类似,但是方法只会保留 nextState 中状态,原 state 不在 nextState 中的状态都会被删除。
replaceState 方法接收 2 个参数:
- nextState:将要设置的新状态,该状态会替换当前的 state。
- callback:可选参数,回调函数。该函数会在 replaceState 设置成功,且组件重新渲染后调用。
设置组件属性,并重新渲染组件。
当我们需要向组件传递数据或通知React.render()组件需要重新渲染,可以使用 setProps() 方法。
setProps 方法接收 2 个参数:
- nextProps,将要设置的新属性,该状态会和当前的props合并
- callback,可选参数,回调函数。该函数会在setProps设置成功,且组件重新渲染后调用。
replaceProps 方法与 setProps 方法类似,但它会删除原有 props。
replaceProps 方法接收 2 个参数:
- nextProps,将要设置的新属性,该属性会替换当前的props。
- callback,可选参数,回调函数。该函数会在replaceProps设置成功,且组件重新渲染后调用。
forceUpdate 方法会使组件调用自身的 render() 函数重新渲染组件,组件的子组件也会调用自己的 render() 函数。但是,组件重新渲染时,依然会读取 this.props 和 this.state,如果状态没有改变,那么 React 只会更新 DOM。
forceUpdate 方法接收 1 个参数:
- callback,可选参数,回调函数。该函数会在组件render()方法调用后调用。
forceUpdate 方法适用于 this.props 和 this.state 之外的组件重绘(如:修改了this.state后),通过该方法通知 React 需要调用 render() 函数。
一般来说,应该尽量避免使用 forceUpdate 方法,而仅从 this.props 和 this.state 中读取状态并由 React 触发 render() 函数的调用。
【拓展】更新 React 组件的方式:
- 可以在节点上再次调用 React.render() 方法,触发组件重新渲染。
- 可以通过 setProps() 方法改变组件属性,触发组件重新渲染。
- 可以使用 forceUpdate() 方法强制触发组件重新渲染,不过应尽量避免这样做。
一般的,react 组件间的通信是通过 属性 自上而下(由父及子) 进行传递的。
React 跨组件通信,一般有两种实现方式:
- 使用 Context 实现 react 跨组件通信。
- 使用组件组合实现 react 跨组件通信(将函数作为子类的组件)。
在 React 中 props 对象可以用来实现父组件向子组件通信。
例如:
React 官方:Context
Context 提供了一种在组件之间共享“应用程序中许多组件都需要的属性”的方式——将这个 “多个组件共享一个全局数据” 放在 Context 对象上。而不必显式地通过组件树的逐层传递 props。
【注意】
请谨慎使用 Context,因为这会使得组件的复用性变差。
如果你只是想避免层层传递一些属性,组件组合 有时候是一个比 context 更好的解决方案。
(1)、Context API
①、React.createContext
创建一个 Context 对象。
createContext 函数:
- 只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。
②、Context.Provider 和 Context.Consumer
每个 Context 对象都会返回一个 Provider 组件,它允许 Consumer 组件订阅 context 的变化。
Provider 组件:
- Provider 接收一个 value 属性,传递给 Consumer 组件。
- 一个 Provider 可以和多个 Consumer 组件有对应关系。
- 多个 Provider 可以嵌套使用,里层的会覆盖外层的数据(就近原则)。
- Provider 使用了与 Object.is 相同的算法,通过新旧值检测来确定变化。
当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。
Consumer 组件:
- Consumer 组件用来订阅 context。
这种方法需要一个函数作为子元素(function as a child)。这个函数接收当前的 context 值,并返回一个 React 节点。传递给函数的 value 值等价于组件树上方离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue。
(2)、Context 的使用案例
①、简单使用 Context 的案例
②、动态使用 Context 的案例
动态切换语言。
当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。例如:
解决办法:将 value 状态提升到父节点的 state 里。例如:
组件组合:react 官方建议组件使用一个特殊的 children prop 来将他们的子组件传递到渲染结果中。(将函数作为子类的组件,参见下文“高阶组件”部分)
例如:
您可以将 ref 指向任何值。但是,ref 最常见的用例是访问 DOM 元素,请:参见这篇文章。
参考:高阶组件
高阶组件 和 函数作为子组件 提供了常规组件复用之外的新的模式去使用组件。
举个例子:
React 高阶组件的实现:
组件与高阶组件的区别:
- 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。
- 高阶组件只是为它封装的组件传递一些额外的功能和数据,不会有自己的UI展现。
高阶组件的优势:
- 高阶组件可以自己获取外部资源,传给封装的组件。
- 高阶组件的控制权是使用者。
- 高阶组件不必复用组件,而是对传入组件进行二次封装,赋予其新的功能和特性。
使用高阶组件的注意事项:
- 不要在 render 方法中使用高阶组件。
- 务必复制原始组件的静态方法。
- Refs 不会被传递。
【注意】
React15.3中新加了一个 PureComponent 类。 类 与 类 都可以用来作为继承的基类,不同之处在于,PureComponent 类功能更强大,它可以减少不必要的 render 渲染次数。
参考:
React 教程:函数作为子组件(Function as Child Components)
将函数作为子组件的组件
“将函数作为子类的组件”是接收一个函数当作子组件的组件。
其原理是:利用了 React 组件的一个内置 children 属性,将函数作为子类的组件来实现。
这得益于 react 的 prop-types 的支持。
举个例子:
【注意】
从 React v15.5 开始 , 助手函数已被弃用,建议使用 库来定义 contextTypes,即你需要手动引入 。
上述代码可以改为:
由上述代码可知:通过函数创建子类组件的组件,可以将 父类组件 和 它们的子类组件 解耦,让设计者决定选用哪些参数,及怎么将参数应用于子类组件。
使用者可能考虑以不同的方式使用该组件,比如:
将函数作为子类的组件的优势:
- 组合组件的开发人员可以快速传递和使用这些属性。
- 函数作为子组件的模式不强制使用者如何利用其值,可以非常灵活的使用。
将函数作为子类的组件 与 react 高阶组件 的比较:
- 使用者不需要创建另一个组件来决定如何应用从 “高阶组件” 传入的属性。高阶组件通常在它们所组成的组件上强制要求属性名。为了解决这个问题,许多“高阶组件”提供者提供了一个选择器函数,允许使用者选择需要的属性名称(想想 redux-connects 选择函数)。在函数作为子组件的模式不存在这个问题。
- 不会污染 “props” 的命名空间,这允许您使用 “Ratio” 组件结合 “Pinch to Zoom” 组件使用,无论它们是否都有计算宽度。高阶组件带有他们对组成的组件施加了隐式约定,不幸的是,这可能意味着 prop 名称冲突。
- 高阶组件在您的开发工具和组件本身中创建一个间接层,例如,一旦包含在高阶组件中,高阶组件中的设置常量将无法访问。例如:,然后由高阶组件包裹,。你的常数就好像死了。如果没有高阶组件提供访问底层组件类的函数,则再也访问不到它。
【参考文章】:
React 官网
React 中文文档
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/yd-react-native/34644.html