一、高阶函数

概念:高阶函数是一个函数,它接收函数作为参数或将函数作为输出返回

  • 举个栗子:
    • 接收函数作为参数 ```javascript function a(x) { x(); } function b() { alert(‘hello’); }

a(b);

  1. - 将函数作为输出返回
  2. ```javascript
  3. function a() {
  4. function b() {
  5. alert('hello');
  6. }
  7. return b;
  8. }
  9. a()();
  • 以上函数a就是一个高阶函数, 用法非常简单, 那么实际开发中又有哪些是高阶函数呢?
    • Array 的 map 、reduce 、filter 等方法
    • Object 的 keys 、values 等方法


二、高阶组件

概念:**高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件**

  • 举个栗子: ```javascript import React from ‘react’; import logo from ‘./logo.svg’; import ‘./App.css’;

class MyComponent extends React.Component{ render(){ return (

Hello React

) } } function HocComponent(WrappedComponent){ return class extends React.Component{ render(){ return } } }

const NewComponent = HocComponent(MyComponent)

function App() { return ( ); }

export default App;

  1. TipHocComponent函数就是一个高阶组件。
  2. <a name="wOL05"></a>
  3. ### 2-1 高阶组件的实现方式-->属性代理
  4. 属性代理的作用:
  5. - 更改prop<br />
  6. - 通过refs获取组件实例<br />
  7. - 抽象state<br />
  8. - WrappedComponent与其他elements包装在一起
  9. <a name="FTHLb"></a>
  10. #### 2-1-1 属性代理-更改props
  11. 在高阶组件中添加新的 props,可以在 WrappedComponent 中通过 this.props.name访问到。
  12. ```javascript
  13. import React from 'react';
  14. import logo from './logo.svg';
  15. import './App.css';
  16. class MyComponent extends React.Component{
  17. render(){
  18. return (<p>Hello React {this.props.name}</p>)
  19. }
  20. }
  21. function HocComponent(WrappedComponent){
  22. return class extends React.Component{
  23. render(){
  24. return <WrappedComponent {...this.props}/>
  25. }
  26. }
  27. }
  28. const NewComponent = HocComponent(MyComponent)
  29. function App() {
  30. return (
  31. <NewComponent name="zhangsan"></NewComponent>
  32. );
  33. }
  34. export default App;

2-1-2 属性代理-通过refs获取组件实例

可以通过引用(ref)访问到 this (WrappedComponent 的实例),但为了得到引用,WrappedComponent 还需要一个初始渲染,意味着你需要在 HOC 的 render 方法中返回 WrappedComponent 元素,让 React 开始它的一致化处理,你就可以得到 WrappedComponent 的实例的引用。

  1. import React from 'react';
  2. import logo from './logo.svg';
  3. import './App.css';
  4. class MyComponent extends React.Component {
  5. say(){
  6. console.log("say say say .....")
  7. }
  8. render() {
  9. return (<p>Hello React {this.props.name}</p>)
  10. }
  11. }
  12. function HocComponent(WrappedComponent) {
  13. return class extends React.Component {
  14. //此处的instance会拿到被包装的组件实例,然后访问被包装的组件对象的属性和方法
  15. proc(instance) {
  16. instance.say();
  17. console.log(instance.props.name)
  18. }
  19. render() {
  20. return <WrappedComponent {...this.props} ref={this.proc} />
  21. }
  22. }
  23. }
  24. const NewComponent = HocComponent(MyComponent)
  25. function App() {
  26. return (
  27. <NewComponent name="zhangsan"></NewComponent>
  28. );
  29. }
  30. export default App;

2-1-3 属性代理-抽取state

  1. import React from 'react';
  2. import logo from './logo.svg';
  3. import './App.css';
  4. class MyInput extends React.Component {
  5. constructor(props){
  6. super(props);
  7. this.state = {
  8. value:""
  9. }
  10. this.handleChange = this.handleChange.bind(this)
  11. }
  12. handleChange(event){
  13. this.setState({
  14. value:event.target.value
  15. })
  16. }
  17. render() {
  18. return (<input value={this.state.value} onChange={this.handleChange}></input>)
  19. }
  20. }
  21. class MyTextArea extends React.Component {
  22. constructor(props){
  23. super(props);
  24. this.state = {
  25. value:""
  26. }
  27. this.handleChange = this.handleChange.bind(this)
  28. }
  29. handleChange(event){
  30. this.setState({
  31. value:event.target.value
  32. })
  33. }
  34. render() {
  35. return (<textarea value={this.state.value} onChange={this.handleChange}></textarea>)
  36. }
  37. }
  38. function HocComponent(WrappedComponent) {
  39. return class extends React.Component {
  40. render() {
  41. return <WrappedComponent/>
  42. }
  43. }
  44. }
  45. const NewInput = HocComponent(MyInput)
  46. const NewTextArea = HocComponent(MyTextArea)
  47. function App() {
  48. return (
  49. <div>
  50. <NewInput></NewInput>
  51. <NewTextArea></NewTextArea>
  52. </div>
  53. );
  54. }
  55. export default App;
  56. //这边代码存在的问题:MyInput和MyTextArea组件都有自己的state和change方法,代码重用性太大
  1. import React from 'react';
  2. import logo from './logo.svg';
  3. import './App.css';
  4. class MyInput extends React.Component {
  5. render() {
  6. return (<input value={this.props.value} onChange={this.props.onChange}></input>)
  7. }
  8. }
  9. class MyTextArea extends React.Component {
  10. render() {
  11. return (<textarea value={this.props.value} onChange={this.props.onChange}></textarea>)
  12. }
  13. }
  14. function HocComponent(WrappedComponent) {
  15. return class extends React.Component {
  16. //将所有被包含组件的state和change都抽取到高阶组件中
  17. constructor(props) {
  18. super(props);
  19. this.state = {
  20. value: ""
  21. }
  22. this.handleChange = this.handleChange.bind(this)
  23. }
  24. handleChange(event) {
  25. this.setState({
  26. value: event.target.value
  27. })
  28. }
  29. render() {
  30. const newProps = {
  31. value: this.state.value,
  32. onChange: this.handleChange
  33. }
  34. return (
  35. //向被包含组件传递属性
  36. <WrappedComponent {...this.props} {...newProps} />
  37. )
  38. }
  39. }
  40. }
  41. const NewInput = HocComponent(MyInput)
  42. const NewTextArea = HocComponent(MyTextArea)
  43. function App() {
  44. return (
  45. <div>
  46. <NewInput></NewInput>
  47. <NewTextArea></NewTextArea>
  48. </div>
  49. );
  50. }
  51. export default App;

2-1-4 属性代理-用其他元素包装组件

为了封装样式、布局或别的目的,可以用其它组件和元素包裹 WrappedComponent。基本方法是使用父组件实现,但通过 HOC 可以得到更多灵活性。

  1. import React from 'react';
  2. import logo from './logo.svg';
  3. import './App.css';
  4. class MyComponent extends React.Component {
  5. render() {
  6. return (<p>被包裹组件</p>)
  7. }
  8. }
  9. function HocComponent(WrappedComponent) {
  10. return class extends React.Component {
  11. render() {
  12. return (
  13. <div style={{color:"red"}}>
  14. <WrappedComponent {...this.props} />
  15. </div>
  16. )
  17. }
  18. }
  19. }
  20. const NewComponent = HocComponent(MyComponent)
  21. function App() {
  22. return (
  23. <div>
  24. <NewComponent></NewComponent>
  25. </div>
  26. );
  27. }
  28. export default App;

2-2 高阶组件的实现方式-反向继承

反向继承的作用:

  • 渲染劫持
  • 操作state
    1. function iiHOC(WrappedComponent) {
    2. return class Enhancer extends WrappedComponent {
    3. render() {
    4. return super.render()
    5. }
    6. }
    7. }
    返回的 HOC 类(Enhancer)继承了 WrappedComponent。之所以被称为 Inheritance Inversion 是因为 WrappedComponent 被 Enhancer 继承了,而不是 WrappedComponent 继承了 Enhancer。在这种方式中,它们的关系看上去被反转(inverse)了。

2-2-1 反向继承-渲染劫持

之所以被称为渲染劫持是因为 HOC 控制着 WrappedComponent 的渲染输出,可以用它做各种各样的事。通过渲染劫持可以:

  1. 在由 render输出的任何 React 元素中读取、添加、编辑、删除 props
  2. 读取和修改由 render 输出的 React 元素树
  3. 有条件地渲染元素树
  4. 把样式包裹进元素树(就像在 Props Proxy 中的那样)
  1. import React from 'react';
  2. import logo from './logo.svg';
  3. import './App.css';
  4. class MyComponent extends React.Component {
  5. render() {
  6. return (<p name="pp">被包裹组件</p>)
  7. }
  8. }
  9. function iiHOC(WrappedComponent) {
  10. return class Enhancer extends WrappedComponent {
  11. render() {
  12. const elementsTree = super.render();
  13. //打印父组件的属性值
  14. console.log(elementsTree.props)
  15. //添加新的 属性返回
  16. let newProps = {style:{color:'red'}};
  17. const props = Object.assign({},elementsTree.props,newProps);
  18. const newElement = React.cloneElement(elementsTree, props, elementsTree.props.children)
  19. return newElement;
  20. }
  21. }
  22. }
  23. const NewComponent = iiHOC(MyComponent)
  24. function App() {
  25. return (
  26. <div>
  27. <NewComponent></NewComponent>
  28. </div>
  29. );
  30. }
  31. export default App;

2-2-2 反向继承-修改state

HOC 可以读取、编辑和删除 WrappedComponent 实例的 state,如果需要,也可以给它添加更多的 state。但是这会搞乱 WrappedComponent 的 state,可能会导致破坏某些东西,通常不建议使用高阶组件修改添加state。

  1. 通过访问 WrappedComponent props state 来做调试。
  2. 这里 HOC 用其他元素包裹着 WrappedComponent,还输出了 WrappedComponent 实例的 props state
  3. export function IIHOCDEBUGGER(WrappedComponent) {
  4. return class II extends WrappedComponent {
  5. render() {
  6. return (
  7. <div>
  8. <h2>HOC Debugger Component</h2>
  9. <p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre>
  10. <p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre>
  11. {super.render()}
  12. </div>
  13. )
  14. }
  15. }
  16. }