zp1112 / blog

地址
http://issue.suzper.com/
36 stars 3 forks source link

react高阶组件和react-redux的高阶组件应用的理解 #8

Open zp1112 opened 6 years ago

zp1112 commented 6 years ago

高阶函数

在伟大的js中,高阶函数通俗的讲指的是一个参数为函数,且return返回值也是函数的函数,即:

const gjfunc = (func) => () => func(); // 定义一个高阶函数
const usegjfunc = gjfunc(() => {console.log(111)}) 调用高阶函数返回一个函数
usegjfunc(); // 使用这个函数

哇偶,好简单,高阶函数著名的应用就是柯里化.

const curry = (func, args) => {
  let len = func.length;
  args = args || [];
  return (...arg) => {
    args.push(...arg);
    if (args.length == len) {
      return func( ...args);
    } else {
      return curry(func,args);
    }
  }
}
curry((a, b) => a + b)(1)(2); // 3
const func1 = curry((a, b) => a + b); // 返回一个函数
const func2 = func1(1); // 返回一个函数
const func3 = func2(2); // 返回一个结果

高阶组件

高阶组件的概念通俗的讲就是一个参数为组件,返回值也是一个组件的函数, wrapperCom = wrapperFunc(wrappedComp);高阶组件 = 高阶函数(组件)

import React, { PureComponent } from 'react';

const WrapperFunc =  (WrappedComp) => 
  class WrapperFunc extends PureComponent {
    constructor(props) {
      super(props);
      this.state = {
        count: 0
      }
    }
    render() {
      return (
        <WrappedComp data = {this.state} {...this.props} />
      )}
  }

class wrappedComp extends PureComponent {
  render() {
    return(
      <div>
        {this.props.data.count}
      </div>
    )}
}
export const WrapperComp = WrapperFunc(wrappedComp);

这里的高阶函数WrapperFunc将wrappedComp组件包裹了一层,处理后再返回新的组件,实现了传入data属性来方便被包裹组件获得count公共状态,后面使用WrapperFunc包裹的任何组件都可以具备这个count。

那么如果我们想要定制count咋办呢,可以将count作为WrapperFunc函数的第二个参数,实现WrapperFunc(wrappedComp, 1)的功能,但是我们参考柯里化,可以使用更佳优雅的格式,将高阶函数再高阶一层,就变成了WrapperFunc(1)(wrappedComp),即WrapperFunc高阶函数,传入count=1作为参数,返回一个高阶函数,传入wrappedComp组件作为参数,返回包裹后的高阶组件。

import React, { Component, PureComponent } from 'react';

const WrapperFunc = (count) => (WrappedComp) => 
  class WrapperFunc extends PureComponent {
    constructor(props) {
      super(props);
      this.state = {
        count
      }
    }
    render() {
      return (
        <WrappedComp data = {this.state} {...this.props} />
      )}
  }

class wrappedComp extends PureComponent {
  render() {
    return(
      <div>
        {this.props.data.count}
      </div>
    )}
}
export const WrapperComp = WrapperFunc(1)(wrappedComp);
export const WrapperComp1 = WrapperFunc(2)(wrappedComp);

高阶组件应用在react-redux中的connect

用过react-redux的都知道,使用方式是这样滴:

<Provider store={store}>
  <App />
</Provider>

// app.js
class App extend PureComponent {
  // ...
]
export default connect(mapStateToProp,mapDispatchToProp)(App);
// 其中mapStateToProp,mapDispatchToProp两个函数分别实现了蒋store里面的state和dispatch转换成组件props里面的属性。

实现一个简单的Provider

Provider 最核心的一点就是实现了store的向下传递,使用了React提供的API getChildContext方法和childContextText声明

import React, { Component } from 'react';
import PropTypes from 'prop-types';

export default class Provider extends Component {
  getChildContext() {
    return {
      store: this.props.store
    };
  }
  render() {
    return this.props.children;
  }
}

Provider.childContextTypes = {
  store: PropTypes.object
}

实现一个简单的connect

import React, { Component, PureComponent } from 'react';
import PropTypes from 'prop-types';

export default function connect(mapStateToProp, mapDispatchToProp) {
  return function(Comp) {
    class WrapperFunc extends PureComponent {
      constructor(props) {
        super(props);
        this.state = {}
      }
      componentDidMount() {
        this.setState({
          ...this.state,
          ...mapStateToProp(this.context.store.getState()),
          ...mapDispatchToProp(this.context.store.dispatch)
        })
      }
      render() {
        return (
          <Comp {...this.state}/>
        )
      }
    }
    WrapperFunc.contextTypes = {
      store: PropTypes.object
    }
    return WrapperFunc;
  }
}

这里的关键步骤是将context里面的store里的state和dispatch传递给mapStateToProp, mapDispatchToProp这两个函数,这两个函数分别需要返回组件需要的状态和action,然后作为Comp组件的props传递下去。因此组件Comp就能在props中拿到store里面所需的东西。Boom!!!

使用自己的connect和Provider

import React from 'react';
import ReactDOM from 'react-dom';
import Provider from './provider';
import { createStore } from 'redux';
import App from './App';
import testStore from './testStore';

if (process.env.NOED_ENV !== 'production') {
  const { whyDidYouUpdate } = require('why-did-you-update');
  whyDidYouUpdate(React);
}
const store = createStore(testStore);
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));

App.js

import connect from './connect';

class App extends PureComponent {
  render() {
    return (
      <div>
        {this.props.connectCount}
      </div>
    );
  }
}
export default connect((state) =>({ connectCount: state.count }), (dispatch) => console.log(333, dispatch))(App);

大功告成,不知道该说啥,觉得自己对react的理解又通透了一些,开心~推荐图书《React进阶之路》