React高阶特性学习

发布于 / Front End / 0 条评论

这几天在看React,学习的路线就是先看了油管上国外Mosh的React Tutorial系列的视频(建议直接英文字幕甚至不用开,有些词汇更能达意),接着我看了胡子课堂的Reactjs小册子,你会发现有了Vue的基础和JS较好的基础之后看React真的过渡起来很快,这篇文章旨在记录下学习React高阶组件的一些特性。

高阶组件

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

const NewComponent = higherOrderComponent(OldComponent)

上一篇文章中我写到了装饰者模式,也就是赋能篇,是不是很像!

重要的事情再重复一次,高阶组件是一个函数(而不是组件),它接受一个组件作为参数,返回一个新的组件。这个新的组件会使用你传给它的组件作为子组件,我们看看一个很简单的高阶组件:

import React, { Component } from 'react'

export default (WrappedComponent) => {
  class NewComponent extends Component {
    // 可以做很多自定义逻辑
    render () {
      return <WrappedComponent />
    }
  }
  return NewComponent
}

现在看来好像什么用都没有,它就是简单的构建了一个新的组件类 NewComponent,然后把传进入去的 WrappedComponent 渲染出来。但是我们可以给 NewCompoent 做一些数据启动工作:

import React, { Component } from 'react'

export default (WrappedComponent, name) => {
  class NewComponent extends Component {
    constructor () {
      super()
      this.state = { data: null }
    }

    componentWillMount () {
      let data = localStorage.getItem(name)
      this.setState({ data })
    }

    render () {
      return <WrappedComponent data={this.state.data} />
    }
  }
  return NewComponent
}

现在 NewComponent 会根据第二个参数 name 在挂载阶段从 LocalStorage 加载数据,并且 setState 到自己的 state.data 中,而渲染的时候将 state.data 通过 props.data 传给 WrappedComponent

这个高阶组件有什么用呢?假设上面的代码是在 src/wrapWithLoadData.js 文件中的,我们可以在别的地方这么用它:

import wrapWithLoadData from './wrapWithLoadData'

class InputWithUserName extends Component {
  render () {
    return <input value={this.props.data} />
  }
}

InputWithUserName = wrapWithLoadData(InputWithUserName, 'username')
export default InputWithUserName

假如 InputWithUserName 的功能需求是挂载的时候从 LocalStorage 里面加载 username 字段作为 <input /> 的 value 值,现在有了 wrapWithLoadData,我们可以很容易地做到这件事情。

只需要定义一个非常简单的 InputWithUserName,它会把 props.data 作为 <input /> 的 value 值。然把这个组件和 'username' 传给 wrapWithLoadDatawrapWithLoadData 会返回一个新的组件,我们用这个新的组件覆盖原来的 InputWithUserName,然后再导出去模块。

别人用这个组件的时候实际是用了被加工过的组件:

import InputWithUserName from './InputWithUserName'

class Index extends Component {
  render () {
    return (
      <div>
        用户名:<InputWithUserName />
      </div>
    )
  }
}

根据 wrapWithLoadData 的代码我们可以知道,这个新的组件挂载的时候会先去 LocalStorage 加载数据,渲染的时候再通过 props.data 传给真正的 InputWithUserName

如果现在我们需要另外一个文本输入框组件,它也需要 LocalStorage 加载 'content' 字段的数据。我们只需要定义一个新的 TextareaWithContent

import wrapWithLoadData from './wrapWithLoadData'

class TextareaWithContent extends Component {
  render () {
    return <textarea value={this.props.data} />
  }
}

TextareaWithContent = wrapWithLoadData(TextareaWithContent, 'content')
export default TextareaWithContent

写起来非常轻松,我们根本不需要重复写从 LocalStorage 加载数据字段的逻辑,直接用 wrapWithLoadData 包装一下就可以了。

我们来回顾一下到底发生了什么事情,对于 InputWithUserName 和 TextareaWithContent 这两个组件来说,它们的需求有着这么一个相同的逻辑:“挂载阶段从 LocalStorage 中加载特定字段数据”。

如果按照之前的做法,我们需要给它们两个都加上 componentWillMount 生命周期,然后在里面调用 LocalStorage。要是有第三个组件也有这样的加载逻辑,我又得写一遍这样的逻辑。但有了 wrapWithLoadData 高阶组件,我们把这样的逻辑用一个组件包裹了起来,并且通过给高阶组件传入 name 来达到不同字段的数据加载。充分复用了逻辑代码。

到这里,高阶组件的作用其实不言而喻,其实就是为了组件之间的代码复用。组件可能有着某些相同的逻辑,把这些逻辑抽离出来,放到高阶组件中进行复用。高阶组件内部的包装组件和被包装组件之间通过 props 传递数据

转载原创文章请注明,转载自: 静沐暖阳 » React高阶特性学习
Not Comment Found