前言
接着上一小节到内容,下面我们会来详细介绍上一节中提到到相关知识。本节我把reactjs hooks和高阶组件放在一起是因为这两块内容都是组件化中相关的内容。高阶组件使用过reactjs的人都应该有所了解,hooks是reactjs 16.8.0新增的属性。想再老项目中使用 hooks完全不用担心兼容性问题,因为hooks 是100% 向后兼容的。本节详细谈谈hooks的 使用方式以及相关概念。
高阶组件
- 先来谈谈高阶组件解决了什么问题:reactjs文档中介绍高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧,由此可见他是用来解决组件复用的一种技巧,也可以说高阶组件是一种参数为组件,返回值为新组件的函数。
作用
- 无副作用: 没有副作用的情况下实现组件的复用,注意没有副作用是关键点。
- 重用函数:高阶组件可以提取出可重用的函数,简你的state和生命周期方法。
- 装饰器:高阶组件可以作为装饰器来使用,如mobx-react
- 渲染劫持:劫持组件的props和state
简单实例
- 我们实际编程中总会遇到多个组件中处理相似的问题,如果把这写组件都写一遍是不是很傻,高阶组件就可以解决这个尴尬。假设我们有A B两个组件,他们的大部分实现都是相同的
import React, { Component } from 'react' class A extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = { data: "data" }; } componentDidMount() { let data = "初始化数据" this.setState({ data }) } handleChange() { this.setState({ data: "改变数据"}); } render() { return (<div><span>这是组件A自己的内容</span> <h2 onClick={this.handleChange.bind(this)}>{this.props.data}</h2></div>) } } class B extends React.Component { constructor(props) { super(props); this.state = { data: "data"}; } componentDidMount() { let data = "初始化数据" this.setState({ data }) } handleChange() { this.setState({ data: "改变数据"}); } render() { return (<div>这是组件B自己的内容 <h2 onClick={this.handleChange.bind(this)}>{this.props.data}</h2></div> ) } }
- 根据两个组件的特性封装高阶组件(将两个组件共通点抽取出来)
import React, { Component } from 'react' const hocBox = WrappedComponent => { return class extends Component{ componentWillMount() { let data = "这是hoc的data" this.setState({ data }) } handleChange = () =>{ this.setState({ data: "改变数据"}); } render() { return <WrappedComponent update={this._alert} {...this.props}/> } } } export default hocBox
- 调用
import hocBox from './hocBox'; class C extends Component{...} C = WrapperHello(C) export default C
使用方式
- 两种常见的用法属性代理(高阶组件通过ComponentClass的props来进行相关操作) 和 继承反转(高阶组件继承自ComponentClass)
- 属性代理:多用于组件的复用,例如装饰器:
复制代码 function WrapperComp(Comp) { class WrapComp extends React.Component { render() { return ( <div> <p>属性代理高阶组件</p> <Comp {...this.props}></Comp> </div> ) } } return WrapComp; } @WrapComp class A extends React.Component { render() { return <h4>hello Jason</h4> } }
此时A组件就相当于如下:
class A extends React.Component { render() { return ( <div> <p>这是react高阶组件特有的元素</p> <h4>hello Jason</h4> </div> ) } }
- 继承反转:组件和包装组件之间的关系是继承关系而不是代理的方式,可以修改原组件的生命周期,渲染流程和业务逻辑.多用于渲染的劫持。
function WrapperComp(Comp) { class WrapComp extends Comp { componentDidMount() { console.log('反向代理高阶组件') } render() { return <Comp {...this.props}></Comp> } } return WrapComp; } @WrapperComp class A extends React.Component { componentDidMount() { console.log('加载完成') } render() { return <h4>反向继承父组件</h4> } }
注意:反向继承的高阶组件中的state和props会覆盖调原组件中的state和props
可能遇到的问题
- 原组件定义的静态方法丢失:如果我们原组件中定义了静态方法
- refs不被传递:在高阶组件中,ref不会被传递。可以使用React.forwardRef来实现ref的传递。
- 组件重新挂载:不要在 render 方法中使用 HOC,因为每次render 是都会创建一个新的高阶组件这将导致该组件及其所有子组件 的状态丢失
hooks
hooks的基本概念我们在上一小节中已经了解过了,本节主要来学习hooks的使用,以及详细分析hooks带来的便利
常用api
- useState:返回一个 state,以及更新 state 的函数。相当于类组件中的state和 setstate
const [state, setState] = useState(initialState);
- useEffect:Effect Hook 可以让你在函数组件中执行副作用操作。类似于类组件中的函数componentDidMount componentDidUpdate
// Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; });
useEffect 可以在组件渲染后实现各种不同的副作用。有些副作用可能需要清除,所以需要返回一个函数:
useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; });
注:通过使用这个 Hook,你可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。详情参见官方文档
作用
- 函数中可以使用类中特有state和setstate
- 减少了类组件的使用,加快了页面渲染。
- 解决了在组件之间复用状态逻辑很难的问题
- 降低组件的复杂度
总结
本章把hooks和高阶组件放在一起来分析,那是因为我觉的这两点都是reactjs在函数式编程上具有创造性的代码书写方式。现今前端逐渐的组件化,项目越来越复杂如果没有采用一个好的架构方式,我认为这将大大缩短项目的存活周期,以及大大降低开发效率。