当前位置:   article > 正文

react 高阶组件 hooks 状态管理 虚拟DOM 组件库_react hooks 高阶组件

react hooks 高阶组件

一.高阶组件

一,高阶组件(Higher Order Component,简称:HOC ):

是 React 中用于重用组件逻辑的高级技术, 它本身不是react中的组件, 而是一个函数, 这个函数接受一个react组件作为参数,并返回一个新组件, 实现了对原有组件的增强和优化, 可以对原有组件中的state, props和逻辑执行增删改操作, 一般用于代码重用和组件增强优化

二,高阶组件的使用场景:

1, 需要代码重用时, react如果有多个组件都用到了同一段逻辑, 这时,就可以把共同的逻辑部分提取出来,利用高阶组件的形式将这段逻辑整合到每一个组件中, 从而减少代码的逻辑重复
2, 需要组件增强优化时, 比如我们在项目中使用的组件有些不是自己写的, 而是从网上撸下来的, 但是第三方写的组件可能比较复杂, 有时不能完全满足需求, 但第三方组件不易修改, 此时也可以用高阶组件,在不修改原始组件的前提下, 对组件添加满足实际开发需求的功能

三,高阶组件的实现方式

1, 属性代理: 通过创建新组建来包裹原始组件, 把原始组件作为新组件的子组件渲染 功能: 可实现对原始组件的 props数据更新 和 组件模板更新
2, 反向继承: 通过创建新组建继承自原始组件, 把原始组件作为父类 功能: 可实现对原始组件的state状态数据更新 和 组件模板更新

四,高阶组件的渲染劫持: 通过高阶组件把原始组件的模板进行修改和替换

1,渲染劫持指对一个组件渲染内容的装饰或修改, 一般通过高阶组件来实现, 把一个组件传入高阶组件, 可以对这个组件的模板进行修改后执行渲染, 也可以阻止组件渲染,并修改组件中的数据和逻辑
2,渲染劫持的应用: 一般用于一些需要登录状态的页面, 在路由请求渲染页面(如订单页)之前, 使用高阶组件判断是否已登录,如果已登录,返回订单页模板, 如果没有登录,返回空,并跳转到登录页

五,高阶组件的实现步骤:
1, 新建高阶组件文件 MyHOC.jsx
2, 在文件中创建函数 函数的参数是一个组件OldCom, 函数的返回值也是一个组件 NewCom

function MyHoc(OldCom){
  return class NewCom extends React.Component{
    render(){
        let newProps = { age: 10, sex: '男' }
        return (
          <OldCom {...newProps} ></OldCom>
        )
    }
  }
}
属性代理()或者(反向继承)
function MyHOC (OldCom){
  return class NewCom extends OldCom{
    componentDidMount() {
      this.setState({ name: '吴亦凡' })
    }
    render() {
      return super.render()
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

3, 导出高阶组件函数

  export default MyHOC
  • 1

4, 在需要使用高阶组件的组件中导入

  import MyHoc1 from "./MyHOC1"
  • 1

5, 在导出组件时,使用高阶组件处理之后,再导出

  export default MyHoc2(MyCom)
  • 1

二.hooks

什么是hooks?

hooks是react新版本提供的组合式API语法, 类似于vue3组合式API

hooks有什么用?

使函数式组件拥有组件状态和生命周期功能, 避免this指向问题, 提高运行效率

总结:在函数式组件中, 使用hooks语法模拟状态数据的步骤

1, 从react中导入语法函数setState
    import React, { useState } from "react"
2, 在函数式组件中, 使用setState创建状态数据
    const [name, setName] = useState("名字")
3, 在组件模板中, 直接调用状态名即可
    自定义状态name: {name}-{age}
4, 使用setState函数返回的更新函数修改状态值, 参数是新值,修改后自动刷新界面
    setName("吴亦凡")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
 useEffect() 这个函数可以用来模拟组件的生命周期函数, 他有两个参数, 
     第一个参数是回调函数, 当组件初始化完成和状态更新时调用
     第二个参数是一个数组,可选, 数组中是状态名, 指定那些状态值更新会触发回调函数
    useEffect(()=>{
         如果不加第二个参数, 初始化时调用, 任何状态更新都会调用
        console.log("组件初始化,或有状态更新ComponentWillUpdate")
    })  
    useEffect(()=>{
         如果第二个参数是空数组, 则只在初始化时调用,状态更新时不会调用
        console.log("ComponentDidMount")
    }, [])
    useEffect(()=>{
         如果第二个参数数组中有状态名, 则只会在数组中的状态更新时调用
        console.log("NameWillUpdate")
    }, [name])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

关于路由跳转

编程式导航跳转

import { useNavigate} from "react-router-dom"
 navigate("/home")
 navigate("/home?name=fanfan&age=20")
 navigate("/home/fanfan/20")
navigate("/home", {
            query: {name: 'fanfna'},
            params: {age: 20},
            data: {sex: false},
            state: {phone: "1243124"}
     })

import {useLocation, useMatch, useSearchParams, useParams} from 
   获取react路由传值信息
    const location = useLocation()
    const [search] = useSearchParams()
    const params = useParams()
     match也可以接收动态路由传值, 需要加参数, 可以重置动态路由字段名
     const match = useMatch("/home/:x/:y")
    console.log(1, location, search.get("name"));
    console.log(2, params);
     console.log(3, match.params);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

三.封装withRouter

在react-router6.0中所有的组件都没有路由信息,也没有widthRouter, 需要使用hooks语法引入路由信息, 所以,我们可以自己封装withRouter高阶组件
由于路由数据是在props中,所以使用属性代理方式

import { useLocation, useParams, useNavigate }  from "react-router-dom"

function myHoc(OldCom){
    // 必须返回函数式组件, 因为hooks语法不能用于类组件
    return ()=>{
        // 从hooks中获取路由信息
        const location = useLocation() // 用于路由url传值和state对象传值
        const params = useParams()  // 用于动态路由传值
        const navigate = useNavigate() // 用于编程式导航跳转
        const tempProps = {location, params, navigate}

        // 返回组件模板
        return <OldCom {...tempProps} />
    }
}
export default myHoc
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

四.react-router 6.5 版本 + hooks 语法实现

提示: 此教程使用react-router 6.5 版本 + hooks 语法实现

1, 下载react的路由模块(默认最新6.5)

npm install react-router-dom --save
  • 1

2, 在入口文件 src/index.js中,从路由模块导入工具组件Router ,并包裹根组件
(注: 哈希模式用HashRouter 历史模式用BrowserRouter)

import { HashRouter } from 'react-router-dom'
ReactDOM.render(
	<HashRouter >
		<App />
	</HashRouter >
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3, 创建路由文件src/router/index.js, 导入需要路由跳转的组件, 并导出路由配置数组

	import Home from "../pages/Home/Home"
	import User from "../pages/User/User"
	export default [
    {
        path: '/',
        element: <Home />
    },
    {
        path: '/user',
        element: <User />
    }
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4, 在app.js中导入路由配置函数useRoutes和路由配置数组routes

import routes from './router/index';
import { useRoutes } from "react-router-dom"
  • 1
  • 2

5, 在函数式组件app中创建路由出口

const routerView = useRoutes(routes)
  • 1

6, 在函数式组件app模板中渲染路由出口

{routerView}
  • 1

7, 在需要跳转的页面, 从路由模块导入工具组件 Link

import {NavLink} from 'react-router-dom'
  • 1

8, 使用NavLink组件执行路由跳转

<NavLink to="/">首页/NavLink>
  • 1

注: 关于路由对象传值的更新

v5:  <Link to={{pathname:'/user', data:{}}}   接收: props.location.data   data名自定义
v6:  <NavLink to="/user" state={{name:'吴亦凡'}}  接收: props.location.state  state名固定
  • 1
  • 2

关于路由监听

import {useEffect} from "react"
useEffect(()=>{
    console.log("路由监听", location.pathname)
},[location])
  • 1
  • 2
  • 3
  • 4

五.状态管理

mobx状态管理

实现有两种写法: 装饰器写法和非装饰器写法, 官方推荐使用装饰器语法

(一): 非装饰器语法:
1, 下载安装模块 mobx 和 mobx-react

cnpm i mobx mobx-react --save
  • 1

2, 创建状态管理文件 /src/store/index.js 导入工具函数

import { observable, action } from "mobx";
  • 1

3, 定义状态管理仓库数据 和 数据更新函数

let appState = observable({ 
	    name: '吴亦凡' 
});
appState.setName = action((value) => {
	    appState.name = value;
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4, 导出状态管理仓库

export default  appState
  • 1

5, 在入口文件index.js中导入状态仓库store和状态更新供应组件Provider

import {Provider} from "mobx-react"
import store from "./store/index"
  • 1
  • 2

6, 在入口文件index.js中,使用Provider 包裹App根组件, 并设置store

<Provider store={store} >
		<HashRouter>
  			<App />
		</HashRouter>
</Provider>
  • 1
  • 2
  • 3
  • 4
  • 5

7, 在使用状态管理的组件中导入inject(注入)函数和observer(观察者)函数, 都是高阶组件

import {inject, observer } from "mobx-react";
  • 1

8, 在导出组件之前,使用高阶组件把store注入props中并添加观察者

export default inject("store")(observer(Home))
  • 1

9, 在组件中通过this.props.store调用状态仓库中的数据或更新函数

this.props.store.name
this.props.store.setName("罗志祥")
  • 1
  • 2

(二): 装饰器语法(推荐):
1, 终端执行指令, 暴漏服务端配置文件和信息

npm run eject
  • 1

2, 在package.json文件下找到bebel属性, 增加下面配置

"plugins": [
      [
        "@babel/plugin-proposal-decorators",
        {
          "legacy": true
        }
      ]
    ]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3, 下载安装模块 mobx 和 mobx-react

cnpm i mobx mobx-react --save
  • 1

4, 创建状态管理文件 /src/store/index.js 导入工具函数

import { observable, action } from "mobx";
  • 1

5, 定义仓库类, 创建仓库数据并设置更新函数

class Store {
    		@observable name = '铁柱';
    		@action setName (option) {
      			this.name = option
    		}
  	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6, 创建仓库对象并导出

export default new Store()
  • 1

7, 在入口文件index.js中导入状态仓库store和状态更新供应组件Provider

import {Provider} from "mobx-react"
import store from "./store/index"
  • 1
  • 2

8, 在入口文件index.js中,使用Provider 包裹App根组件, 并设置store

<Provider store={store} >
		<HashRouter>
  			<App />
		</HashRouter>
</Provider>
  • 1
  • 2
  • 3
  • 4
  • 5

9, 在使用状态管理的组件中导入inject(注入)函数和observer(观察者)函数, 都是高阶组件

import {inject, observer } from "mobx-react";
  • 1

10, 在组件创建时, 使用装饰器语法store注入props中并添加观察者

export default 
@inject("store") 
@observer 
class Home extends React.Component{}
  • 1
  • 2
  • 3
  • 4

11, 在组件中通过this.props.store调用状态仓库中的数据或更新函数

this.props.store.name
this.props.store.setName("lisi")
  • 1
  • 2

注: 如果状态数据更新,视图没刷新, 且没报错, 可能是mobx和mobx-react版本不匹配, 注意版本 “mobx”: “^5.15.4”, “mobx-react”: “^6.3.1”

6.redux状态管理

第一步: 创建仓库 vuex ===== redux

1, 下载安装 redux 模块 和 react-redux模块

cnpm install redux --save
cnpm install react-redux --save
  • 1
  • 2

2,创建数据仓库文件 src/store/index.js 从redux导入创建函数creatStore, 合并函数combineReducers

import { createStore, combineReducers } from "redux";
  • 1

3, 定义reducer函数

function countReducer(state = 0, action){
    	    switch (action.type) {
       	        case "ADD":
           		return state + action.number
        	        default:
           		return state 
    	    }
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4, 把多个reducer函数合并成一个

var reducers = combineReducers({
		count: countReducer,
})
  • 1
  • 2
  • 3

5, 创建并导出数据仓库

export default createStore(reducers)
  • 1

第二步: 读取仓库数据

1, 在入口文件index.js中导入状态仓库store和状态更新供应组件Provider

import { Provider } from "react-redux";
import store from "./store/index";
  • 1
  • 2

2, 在入口文件index.js中,使用Provider 包裹App根组件, 并设置store

<Provider store={store}>
  	        <App />
</Provider>
  • 1
  • 2
  • 3

3, 在使用状态管理的组件中导入状态仓库联合函数connect (高阶组件)

import { connect } from "react-redux";
  • 1

4, 在导出组件之前,创建状态数据映射函数映射状态数据

function mapState(store){
    	        return {
        		name: store.name,
    	        }
	}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5, 在导出组件之前, 使用联合函数connect把仓库中的状态数据拼接到组件的props中

MyCom= connect(mapState)(MyCom)
  • 1

6, 在组件中通过this.props调用状态仓库中的数据

{this.props.name}
  • 1

第三步: 更新仓库数据

1, connect高阶组件会向当前组件props中传入dispatch函数,用于状态数据的更新, 参数对应reducer函数的第二个参数action
在需要更新的位置, 更新仓库中的数据

this.props.dispatch({
       	    type: "CHANGE",
        	    value: this.refs.input.value
   	 })
  • 1
  • 2
  • 3
  • 4

附加: 监听仓库中数据的更新
2, 在需要监听更新的组件中导入状态仓库

import store from "../../store/index"
store.subscribe(()=>{
        	    console.log("数据已更新")
    	})
  • 1
  • 2
  • 3
  • 4

注1: store.subscribe() 添加监听后会返回一个函数, 调用返回的函数可以结束监听

注2: 如果报hooks的错,请降低react-redux版本为5.0重试

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/275086
推荐阅读
相关标签
  

闽ICP备14008679号