likes
comments
collection
share

❤React体系07-reder-props和高阶组件介绍

作者站长头像
站长
· 阅读数 67

❤React体系07-reder-props和高阶组件介绍

1、组件复用的两种方式 (利用React自身特点演化成的固定模式)

render-props和高阶组件模式

(1)render-props模式

将复用的state和操作state的方法封装到一个组件中

思路分析 思路:将要复用的state和操作stae的方法封装到一个组件中

问题1:如何拿到该组件中复用的state

在使用组件时,添加一个值为函数的prop,通过函数参数 来获取(需要组件内部实现 )

问题2:如何渲染任意的UI?

使用该函数的返回值作为要渲染的UI内容

<Mouse render={(mouse)=>{

            }}></Mouse>

<Mouse render={(mouse)=>{
    <p>鼠标当前位置: x: {mouse.x} y: {mouse.y}</p>

}}></Mouse>

(2)高阶组件

高阶组件( higher-order component ,HOC )是 React 中复用组件逻辑的一种进阶技巧。它本身并不是 React 的 API,而是一种 React 组件的设计理念,众多的 React 库已经证明了它的价值,例如耳熟能详的 react-redux。

高阶组件的概念其实并不难,我们能通过类比高阶函数迅速掌握。高阶函数是把函数作为参数传入到函数中并返回一个新的函数。这里我们把函数替换为组件,就是高阶组件了。

const EnhancedComponent = higherOrderComponent(WrappedComponent);

2、reder-props 模式的使用

(1)简单介绍

reder-props 模式是React之中一种将组件逻辑与 UI 展示分离的思想,从这个理解上我们可以看出,这肯定是将组件和逻辑进行抽离,本质上就是复用

简单归纳一下可以分为:

组件的状态管理( reducer 函数处理)

组件传递状态和操作函数(props控制)

先来手写一个增减看看我们如何使用这种reder-props 模式

增减组件

import React, { useReducer } from 'react';

// 定义 reducer 函数
const counterReducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

const Counter = () => {
  // 创建状态和 dispatch 函数
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>增夹</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>D减少</button>
    </div>
  );
};

export default Counter;

总结:

上述过程中我们利用Counter(父组件拟定为)使用 useReducer 钩子函数来创建一个状态和 dispatch 函数,然后将这个状态和 dispatch 函数通过 props 传递给子组件(也就是counterReducer) 通过 props 传递的状态进行对应的操作,从而实现组件的增减和状态管理!

鼠标移动事件组件

接下来手写一个render-props 鼠标移动事件的组件


import React from 'react';
// render props 模式
class Mouse extends React.Component{
    // 鼠标位置state
    state={
        x:0,
        y:0
    }
    // 鼠标移动事件的处理程序
    handleMouse= e =>{
        this.setState({
            x:e.clientX,
            y:e.clientY,
        })
    }
    // 监听鼠标移动事件
    componentDidMount(){
        window.addEventListener('mousemove',this.handleMouse)
    }
    render(){
        return this.props.render(this.state);
    }
}
class Appps extends React.Component{
    render() {
        return (
            <div>
                <h1 >render props模式</h1>
                <Mouse render={(mouse)=>{
                    return (<p>鼠标位置x:{mouse.x}鼠标位置y:{mouse.y}</p>)
                }}></Mouse>
            </div> 
            )
    }
}
export default Appps

(2) 复用组件

使用过程

演示Mouse组件的复用

Mouse组件负责:封装复用的状态逻辑代码(1.状态 2.操作状态的方法)

状态:鼠标坐标(xy)

操作状态的方法:鼠标移动事件

传入的render prop负责:使用复用的状态来渲染UI结构

class Mouse extends React.Component {
        render(){
            return this.props.render(this.state)
        }
}

<Mouse render={(mouse)=><p>鼠标位置:{mouse.x},{mouse.y}</p>}></Mouse>
  • 案例源码
// 01 导入图片
import img from './images/cat.png'

// 02复用组件
 <Mouse render={(mouse)=>{
       return (<div><img src={img} alt="猫咪" style={{
             position:'absolute',
             top:mouse.y-5,
             left:mouse.x-5
    }}/></div>)
}}></Mouse>

(3)推荐写法

使用children的写法

还是上面的增减,我们可以先写一个demo看看如何写


import React from 'react';

// 父组件
const ParentComponent = () => {
  return (
    <div>
      <h2>Parent Component</h2>
      {/* 使用 children 属性传递一个函数 */}
      <ChildComponent>
        {({ count, increment, decrement }) => (
          <div>
            <p>Count: {count}</p>
            <button onClick={increment}>Increment</button>
            <button onClick={decrement}>Decrement</button>
          </div>
        )}
      </ChildComponent>
    </div>
  );
};

// 子组件
const ChildComponent = ({ children }) => {
  const [count, setCount] = React.useState(0);

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);

  // 调用 children 函数并传递参数
  return children({ count, increment, decrement });
};

export default ParentComponent;

父组件将一个函数作为 ChildComponent 的子元素来传递数据和操作函数。

子组件在内部调用这个函数并传递了当前的 count 值以及两个操作函数 increment 和 decrement。

从而实现了在 React 中使用 children 属性来实现 render props 模式的效果(如果你想用的话尽量肯定推荐你写这种!

联想到我们平时使用的一些UI自己写的组件库,这个复用性大大提高啊!

改良鼠标移入移出组件

<Mouse>
    {mouse=>{
        return (<p>鼠标位置x:{mouse.x}鼠标位置y:{mouse.y}</p>)
    }}
</Mouse>

<Mouse>
    {mouse=>{
        return (<div><img src={img} alt="猫咪" style={{
            position:'absolute',
            top:mouse.y-5,
            left:mouse.x-5
        }}/></div>)
    }}
</Mouse>

(4) 代码优化

① 与组件同级别给组件添加校验

❤React体系07-reder-props和高阶组件介绍

// 01 导入
import ProtoTypes from 'prop-types';

// 02 添加校验
Mouse.protoTypes={
    children:ProtoTypes.func.isRequired
}

缺失情况下: ❤React体系07-reder-props和高阶组件介绍 报错: ❤React体系07-reder-props和高阶组件介绍

(2)优化部分

1.添加props校验

2.组件卸载时候移除mousemove事间绑定

Musee.propTypes={shildern:PropTypes.cunc.isRequired}

componentWillUnmounted(){
    windows.removeEventListener('mousemove',this.handleMuoseMove)
}

② 在组件解绑时候应该进行移除

❤React体系07-reder-props和高阶组件介绍

class Mouse extends React.Component{
    // 鼠标位置state
    state={
        x:0,
        y:0
    }
    // 鼠标移动事件的处理程序
    handleMouse= e =>{
        this.setState({
            x:e.clientX,
            y:e.clientY,
        })
    }
    // 监听鼠标移动事件
    componentDidMount(){
        window.addEventListener('mousemove',this.handleMouse)
    }
    // 组件卸载时候解绑
    componentWillUnmount(){
        window.removeEventListener('mousemove',this.handleMouse)
    }
    render(){
        return this.props.children(this.state);
    }
}

3、高阶组件

(1) 什么是高阶组件

高阶组件是一个函数。传递给它一个组件,返回一个新的组件。

官方定义:

高阶组件的英文是 Higher-Order Components,简称为 HOC;

官方的定义: 高阶组件是一个参数为组件,并且返回值为新组件函数;

const EnhancedComponent = enhance( MyComponent )

enhance()这个函数就是我们的高阶组件,对MyComponent做出各种处理

高阶函数

高阶函数: 如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。

1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。

2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。

常见的高阶函数有:Promise、setTimeout、arr.map()等等

(2) 优势

  1. 代码复用:逻辑封装和重复代码的使用
  2. 状态提升:多个组件共享状态提升到更高层级的组件中,并通过 props 将状态传递给需要的子组件。
  3. 渲染劫持:劫持组件渲染过程,对组件的 props 进行一些转换或过滤,比如我们项目常见的一些对于年龄的判断。
  4. 访问 Refs:访问传入的组件的 refs,对其进行操作。
  5. 控制组件生命周期:不修改原始组件代码的情况下控制组件的生命周期。

(3) 高阶组件实践

比如常见的菜单是否展开:

import React from 'react';
// 高阶组件
function withAuth(Component) {
  return function(props) {
    const isMenushow = true; // 假设菜单已展示
    return <Component {...props} isMenushow={isMenushow} />;
  };
}

// 使用高阶组件
function MyComponent(props) {
  return (
    <div>
      {props.isMenushow ? '菜单已展示' : '菜单未展示'}
    </div>
  );
}

const EnhancedComponent = withAuth(MyComponent);

// 渲染增强后的组件
function App() {
  return (
    <div>
      <EnhancedComponent />
    </div>
  );
}

export default App;

分析:

高阶组件是一个函数,接受要包装的组件,返回包装以后的组件。

内部创建一个类组件,之后在其中提供复用的状态以及代码,通过props将复用的状态传递给被包装的组件WrappedComponenet

(4)高阶组件使用

写一个简单的一个高阶组件

import React from 'react';

// 高阶组件
const withLogging = (WrappedComponent) => {
  // 返回一个新的组件
  return class WithLogging extends React.Component {
    componentDidMount() {
      console.log('Component is mounted');
    }

    componentWillUnmount() {
      console.log('Component is unmounted');
    }

    render() {
      // 渲染被包裹的组件,并将所有传递给高阶组件的 props 传递给它
      return <WrappedComponent {...this.props} />;
    }
  };
};

// 被包裹的组件
const MyComponent = ({ message }) => {
  return <div>{message}</div>;
};

// 使用高阶组件增强 MyComponent
const MyComponentWithLogging = withLogging(MyComponent);

// 使用增强后的组件
const App = () => {
  return <MyComponentWithLogging message="Hello, World!" />;
};

export default App;

更多高阶函数在后续之中进行不断完善!

转载自:https://juejin.cn/post/7362084371058114560
评论
请登录