专栏名称: 前端JavaScript
分享 | 学习 | 交流 | 原创 分享是学习的开始;学习不必要从头开始,是从现在开始;交流能沟通你我,提高你的学识;期待你的加入!!! web前端技术交流,JavaScript,HTML5,CSS3……
目录
相关文章推荐
51好读  ›  专栏  ›  前端JavaScript

[S3-E389]面向初学者的高阶组件介绍

前端JavaScript  · 公众号  · Javascript  · 2017-07-21 07:27

正文


 本文:【第3章第389回】 更多文章点击 目录 查看


前言

写这篇文章的起因是其他关于高阶组件(Higher-Order Components)的文章,包含官方文档,都令初学者感到相当困惑。我知道有高阶组件这样一个东西,但不知道它到底有什么用。所以,想通过一篇文章来对高阶组件有一个更好的理解。


在此之前,我们需要先来讲一下 JavaScript 中的函数。


ES6 箭头函数简介

接下来将提供一些箭头函数的简单示例,如果之前没有使用过,可以认为它们与普通函数基本一致。下面的代码会展示箭头函数与普通函数的区别。


function () {

  return 42

}


// same as:

() => 42


// same as:

() => {

  return 42

}


function person(name) {

  return { name: name }

}


// same as:

(name) => {

  return { name: name }

}

阅读 MDN 的箭头函数文档 了解更多信息。


作为值得函数与部分调用

就像是数字、字符串、布尔值 一样,函数也是值,意味着可以像传递其他数据一样传递函数,可以将函数作为参数传递给另外一个函数。


const execute = (someFunction) => someFunction()


execute(() => alert('Executed'))


也可以在在函数中返回一个函数:

const getOne = () => () => 1


getOne()()


之所以在 getOne 后面有两个 () ,是因为第一个返回的返回值是一个函数。如下:


const getOne = () => () => 1


getOne

//=> () => () => 1


getOne()

//=> () => 1


getOne()()

//=> 1


从函数返回函数可以帮助我们追踪初始输入函数。例如,下面的函数接受一个数字作为参数,并返回一个将该参数乘以新参数的函数:


const multiply = (x) => (y) => x * y


multiply(5)(20)


这个示例跟上述 getOne 一样,在下面这个例子,让 x = 5,y = 20。


const multiply = (x) => (y) => x * y


multiply

//=> (x) => (y) => x * y


multiply(5)

//=> (y) => 5 * y


multiply(5)(20)

//=> 5 * 20


在只传入一个参数调用 multiply 函数时,即部分调用该函数。比如,multiply(5) 讲得到一个将其输入值乘以 5 的函数,multiply(7) 将得到一个将其输入值乘以 7 的函数。依此类推。通过部分调用可以创建一个预定义功能的新函数:


const multiply = (x) => (y) => x * y


const multiplyByFive = multiply(5)

const multiplyBy100 = multiply(100)


multiplyByFive(20)

//=> 100

multiply(5)(20)

//=> 100


multiplyBy100(5)

//=> 500

multiply(100)(5)

//=> 500


一开始看起来似乎没什么用,但是,通过部分调用这种方式可以编写可读性更高,更易于理解的代码。举个例子,可以用一种更清晰的方式来代替 style-components 的函数插入语法。


// before

const Button = styled.button`

  background-color: ${({ theme }) => theme.bgColor}

  color: ${({ theme }) => theme.textColor}

`


// after

const fromTheme = (prop) => ({ theme }) => theme[prop]


const Button = styled.button`

  background-color: ${fromTheme("bgColor")}

  color: ${fromTheme("textColor")}

`



我们创建一个接受一个字符串作为参数的函数 fromTheme("textColor"):它返回一个接受具有 theme 属性的对象的函数:({ theme }) => theme[prop],然后再通过初始传入的字符串 "textColor" 进行查找。我们可以做得更多,写类似的 backgroundColor 和 textColor 这种部分调用 fromTheme 的函数:


const fromTheme = (prop) => ({ theme }) => theme[prop]

const backgroundColor = fromTheme("bgColor")

const textColor = fromTheme("textColor")


const Button = styled.button`

  background-color: ${backgroundColor}

  color: ${textColor}

`



高阶函数

高阶函数的定义是,接受函数作为参数的函数。如果曾经使用过类似 map 这样的函数,可能已经很熟悉高阶函数。如果不熟悉 map,它是一个数组遍历的方法,接受一个函数作为参数应用到数组中的每个元素。例如,可以像这样对一个数组作平方:


const square = (x) => x * x


[1, 2, 3].map(square)

//=> [ 1, 4, 9 ]


可以实现一个我们自己的 map 版本来说明这个概念:


const map = (fn, array) => {

  const mappedArray = []


  for (let i = 0; i < array.length; i++) {

    mappedArray.push(

      // apply fn with the current element of the array

      fn(array[i])

    )

  }


  return mappedArray

}


然后再使用我们的 map 版本来对一个数组作平方:


const square = (x) => x * x


console.log(map(square, [1, 2, 3, 4, 5]))

//=> [ 1, 4, 9, 16, 25 ]


译者注:我们也可以将 map 方法从对象中解耦出来:


const map = (fn, array) => Array.prototype.map.call(array, fn)

这样也可以像上述例子一样调用。或者更函数式的做法,再来点柯里化:


const map = array => fn => Array.prototype.map.call(array, fn)

或者是返回一个

  • 的 React 元素数组:


    const HeroList = ({ heroes }) => (

     

          {map((hero) => (

           

    • {hero}
    •     ), heroes)}

       

    )


      "Wonder Woman",

      "Black Widow",

      "Spider Man",

      "Storm",

      "Deadpool"

    ]/>

    /*=> (

     

         

    • Wonder Woman
    •    

    • Black Widow
    •    

    • Spider Man
    •    

    • Storm
    •    

    • Deadpool
    •  

      )*/


    高阶组件

    我们知道,高阶函数是接受函数作为参数的函数。在 React 中,任何返回 JSX 的函数都被称为无状态函数组件,简称为函数组件。基本的函数组件如下所示:


    const Title = (props) =>

    {props.children}


    Higher-Order Components(HOCs) for React Newbies

    //=>

    Higher-Order Components(HOCs) for React Newbies


    高阶组件则是接受组件作为参数并返回组件的函数。如何使用传入组件完全取决于你,甚至可以完全忽视它:


    // Technically an HOC

    const ignore = (anything) => (props) =>

    :)


    const IgnoreHeroList = ignore(HeroList)

    //=>

    :)


    可以编写一个将输入转换成大写的 HOC:


    const yell = (PassedComponent) =>

      ({ children, ...props }) =>

       

          {children.toUpperCase()}!

       


    const Title = (props) =>

    {props.children}

    const AngryTitle = yell(Title)


    Whatever

    //=>

    WHATEVER!


    你也可以返回一个有状态组件,因为 JavaScript 中的类不过是函数的语法糖。这样就可以使用到 React 生命周期的方法,比如 componentDidMount。这是 HOCs 真正有用的地方。我们现在可以做一些稍微有趣点的事,比如将 HTTP 请求的结果传递给函数组件。


    const withGists = (PassedComponent) =>

      class WithGists extends React.Component {

        state = {

          gists: []

        }


        componentDidMount() {

          fetch("https://api.github.com/gists/public")

          .then((r) => r.json())

          .then((gists) => this.setState({

            gists: gists

          }))

        }


        render() {

          return (

           

              {...this.props}

              gists={this.state.gists}

            />

          )

        }

      }



    const Gists = ({ gists }) => (

     

    {JSON.stringify(gists, null, 2)}

    )


    const GistsList = withGists(Gists)


    //=> Before api request finishes:

    //

    // 

    //=> After api request finishes:

    //

    //  { /* … */ },

    //  { /* … */ },

    //  { /* … */ }

    // ]} />


    withGists 会传递 gist api 调用的结果,并且你可以在任何组件上使用。点击这里 可以看到一个更加完整的例子。


    结论:高阶组件是 🔥🔥🔥

    react-redux 也是使用 HOC, connect 将应用 store 的值传递到“已连接” 的组件。它还会执行一些错误检查和组件生命周期优化,如果手动完成将导致编写大量重复代码。


    如果你发现自己在不同地方编写了大量的代码,那么也可以将代码重构成可重用的 HOC。


    HOCs 非常具有表现力,可以使用它们创造很多很酷的东西。


    尽可能地保持你的 HOC 简单,不要编写需要阅读长篇大论才能理解的代码。



    转自:https://segmentfault.com/a/1190000010260169

    作者:离独逸 


    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    如果文章不错,请转发的朋友圈!

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

     ==========阅读原文==========