zoukankan      html  css  js  c++  java
  • React高阶组件 和 Render Props

    高阶组件

    本质

    本质是函数,将组件作为接收参数,返回一个新的组件。HOC本身不是React API,是一种基于React组合的特而形成的设计模式。

    解决的问题(作用)

    • 一句话概括:功能的复用,减少代码冗余
    • 进一步解释:在实际情况中,多个组件可能会做某些相同的事情,有着相同的功能,存在大量的代码冗余。我们可以将这部分功能拆分出来,每个组件尽量只保留自己独有的作用,通过HOC生成我们最终需要的组件。

    实现方法:

    无论哪种方法,都是在HOC函数内定义新的组件,在新的组件内做一些公共的功能和事情

    1. 属性代理
    2. 反向继承

    属性代理

    这是最常规的写法,原理等同于ES7装饰器、Python装饰器。函数传入的参数,除了原组件,还可以定义其他的参数,通过这些参数来区别每个实际组件。比如,公共的功能是获取数据。获取数据这件事情是相同的,但获取的内容不同。如何决定最后生成的组件获取各自指定的内容呢?通过函数传参。

    // 示例用localstorage代替如网络请求等的异步操作
    localStorage.setItem("comment","asuiklfhs");  
    localStorage.setItem("read","123");
    
    class Comment extends React.Component {
    
       render() {
          return (
             <div>
                {this.props.data}
             </div>
          )
       }
    }
    
    class Read extends React.Component {
    
       render() {
          return (
             <div>
                {this.props.data}
             </div>
          )
       }
    }
    
    const HOCTest = (key) => {
       return (WrappedComponent) => {
          return class NewComponent extends React.Component{
    
             constructor(props) {
                super(props);
                this.state = {
                   data: ''
                };
             }
    
             componentDidMount() {
                const data = localStorage.getItem(key);
                this.setState({
                   data
                });
             }
    
             render() {
                console.log(this.myRef);
                return (
                   <div ref={(e)=>{this.myRef = e;}}>
                      <WrappedComponent {...this.props} data={this.state.data} />
                   </div>
                );
             }
          }
       }
    };
    
    const CommentNew = HOCTest("comment")(Comment);  // HOC生成新组件
    const ReadNew = HOCTest("read")(Read);
    
    class Root extends React.Component {
       render() {
          return (
             <div className="App">
                Hello World
                <CommentNew/>
                <ReadNew/>
             </div>
          );
       }
    }

    在这里,React第三方的组件库通常使用 函数柯里化 的写法。对于使用者来说,改变了调用函数时的传参方式,更加容易理解。即:原本调用函数wrapper(Component, params1, params2)  柯里化后调用wrapper(params1,params2)(Component)

    属性代理常见的作用:

    1. 操作props
    2. 通过Refs访问组件实例
    3. 提取state
    4. 组合更多的html元素

    反向继承

    顾名思义,就是将返回的组件继承了原组件。它允许生成的组件通过this获取原组件,意味着可以获取到state,props,生命周期钩子,以及render

    localStorage.setItem("comment","asuiklfhs");
    localStorage.setItem("read","123");
    
    class Comment extends React.Component {
       constructor(props){
          super(props);
          this.state = {id:"comment"}
       }
    
       componentDidMount() {
          console.log("Comment DidMount");
       }
    
       render() {
          return (
             <div>
                {this.props.data}
             </div>
          )
       }
    }
    
    class Read extends React.Component {
       constructor(props){
          super(props);
          this.state = {id:"read"}
       }
    
       render() {
          return (
             <div>
                {this.props.data}
             </div>
          )
       }
    }
    
    const HOCTest = (key) => {
       return (WrappedComponent) => {
          return class NewComponent extends WrappedComponent{
    
             componentDidMount() {
                console.log("HOC DidMount");
                const data = localStorage.getItem(key);
                this.setState({
                   ...this.state,
                   data
                });
             }
    
             render() {
                return (
                   <WrappedComponent data={this.state.data}/>
                );
             }
          }
       }
    };
    
    const CommentNew = HOCTest("comment")(Comment);
    const ReadNew = HOCTest("read")(Read);
    
    class Root extends React.Component {
    
       render() {
          return (
             <div className="App">
                Hello World
                <CommentNew/>
                <ReadNew/>
             </div>
          );
       }
    }

    作用:

    1. 渲染劫持。由于新组件可以控制原组件的render方法,可以做各种控制渲染的操作。
    2. 操作state

    注意点:

    1. 不要在函数内修改原组件
    2. 使用反向继承方式时,会丢失原本的显示名
    3. 不要在render函数中使用HOC

    高阶组件的缺点:

    1. 难以溯源。如果原始组件A通过好几个HOC的构造,最终生成了组件B,不知道哪个属性来自于哪个HOC,需要翻看每个HOC才知道各自做了什么事情,使用了什么属性。
    2. props属性名的冲突。某个属性可能被多个HOC重复使用。
    3. 静态构建。新的组件是在页面构建之前生成,先有组件,后生成页面。

    Render Props 

    作用

    1. 功能的复用,与HOC类似。
    2. 组件间数据的单向传递。

    什么是Render Props?

    是一个用于告知组件要渲染什么内容的函数属性。该函数返回一个组件,是渲染出来的内容。

    class Cat extends React.Component {
      render() {
        const mouse = this.props.mouse;
        return (
          <p>位置:x:{ mouse.x } y: { mouse.y }</p>
        );
      }
    }
    
    class Mouse extends React.Component {
      constructor(props) {
        super(props);
        this.handleMouseMove = this.handleMouseMove.bind(this);
        this.state = { x: 0, y: 0 };
      }
    
      handleMouseMove(event) {
        this.setState({
          x: event.clientX,
          y: event.clientY
        });
      }
    
      render() {
        return (
          <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
    
            {/*
              Instead of providing a static representation of what <Mouse> renders,
              use the `render` prop to dynamically determine what to render.
            */}
            {this.props.render(this.state)}
          </div>
        );
      }
    }
    
    class MouseTracker extends React.Component {
      render() {
        return (
          <div>
            <h1>移动鼠标!</h1>
            <Mouse render={mouse => (
              <Cat mouse={mouse} />
            )}/>
          </div>
        );
      }
    }

    通过以上Demo可以看到,Mouse组件通过render属性(属性名也可以是别的名字),指定了渲染哪个子组件,并且子组件可以接收参数值,进而实现内部逻辑。

    再进行分析,发现Mouse组件提供可变数据源,是一个基础数据的提供者,最关键的代码是:

    {this.props.render(this.state)}

    通过render属性,将数据传递给另外一个组件。至于这个数据拿来干什么,怎么去渲染,就不是它管的事情了。

    因此,整个页面很多地方可能需要用到鼠标坐标数据,以上的例子就可以实现功能的复用。

    相对高阶组件的优点:

    1. 不用担心props的命名冲突的问题
    2. 可以溯源,子组件的props一定来自父组件。
    3. 是动态构建的,页面在渲染后,可以动态地决定渲染哪个组件。
    4. 所有能用HOC完成的事情,Render Props都可以做,且更加灵活。
    5. 除了功能复用,还可以用作两个组件的单向数据传递。
  • 相关阅读:
    普通javaBean获取Spring托管对象
    java 线程安全问题
    MySQL之alter语句用法总结
    Maven pom.xml配置
    Srping框架初识
    win7(64位)下memcache安装时报错“ failed to install service or service already installed”
    activemq 使用
    elasticsearch plugin
    logback.xml 实例
    Intellij IDEA 插件
  • 原文地址:https://www.cnblogs.com/V587Chinese/p/11444842.html
Copyright © 2011-2022 走看看