最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Overriding React Component's prototype method - Stack Overflow

programmeradmin0浏览0评论

Let's say I have the following React Component class:

class SayHello extends React.Component {
    constructor(props) {
        super(props);

        this.handleOnClick = this.handleOnClick.bind(this);
    }

    render() {
        return <div onClick={this.handleOnClick}>Click Me</div>;
    }

    handleOnClick() {
        console.log("clicked");
    }
}

What I want to do is create a higher order ponent that knows about the handleOnClick in SayHello but before calling SayHello's handleOnClick, I want it to execute some code I pass in first (i.e. I want to run code that logs something in my server).

Is there a React pattern for doing something like this?

EDIT:

I want to provide some more context here. I want my higher order ponent to be dynamic in terms of which methods to call. For example, sometimes it might be handleOnClick but other times it might be handleOnSomethingElse.

Let's say I have the following React Component class:

class SayHello extends React.Component {
    constructor(props) {
        super(props);

        this.handleOnClick = this.handleOnClick.bind(this);
    }

    render() {
        return <div onClick={this.handleOnClick}>Click Me</div>;
    }

    handleOnClick() {
        console.log("clicked");
    }
}

What I want to do is create a higher order ponent that knows about the handleOnClick in SayHello but before calling SayHello's handleOnClick, I want it to execute some code I pass in first (i.e. I want to run code that logs something in my server).

Is there a React pattern for doing something like this?

EDIT:

I want to provide some more context here. I want my higher order ponent to be dynamic in terms of which methods to call. For example, sometimes it might be handleOnClick but other times it might be handleOnSomethingElse.

Share edited Apr 13, 2017 at 19:29 wmock asked Apr 13, 2017 at 18:38 wmockwmock 5,5025 gold badges43 silver badges62 bronze badges 3
  • Is the custom "before ponent method" code dynamic as well? – joews Commented Apr 13, 2017 at 19:36
  • Yes in the sense that I would want it to just mirror the method that's in the SayHello ponent. So if I'm trying to override SayHello's handleOnClick, then it'd be beforeHandleOnClick, and if its SayHello's handleSomethingElse, it'd be beforeHandleSomethingElse. – wmock Commented Apr 13, 2017 at 19:41
  • OK, I've added another answer. I left the first because I think it will help others. – joews Commented Apr 13, 2017 at 20:08
Add a ment  | 

2 Answers 2

Reset to default 6

A higher-order ponent is a function that takes a ponent argument and returns a new ponent.

This function returns a ponent with a decorated handleClick method:

// A higher-order ponent that runs some code before
// the given ponent's `handleClick` method
function wrapHello(ponentClass) {
  return class wrapped extends ponentClass {
    beforeHandleClick() {
      console.log("I run first!")
    }

    handleClick(...args) {
      this.beforeHandleClick()
      super.handleClick(...args)
    }
  }
}

This pattern is neat because it isn't specific to React at all; it's just a pure function. That means it's easy to test and reason about.

Here's a test harness that doesn't use React:

function wrapHello(ponentClass) {
  return class wrapped extends ponentClass {
    beforeHandleClick() {
      console.log("I run first!")
    }

    handleClick(...args) {
      this.beforeHandleClick()
      super.handleClick(...args)
    }
  }
}

class SayHello {
  handleClick() {
    console.log("handleClick")
  }
}

const WrappedHello = wrapHello(SayHello)
new WrappedHello().handleClick()

You need something like a dynamic mixin.

This higher-order ponent takes a Component class and an Object of decorator methods.

The HOC wraps each method that has a matching decorator. These methods call the decorator then call through to the original ponent method. Non-decorated methods are unchanged.

// Higher-order ponent
function decorateMethods(ponentClass, decorators) {
  class decoratedClass extends ponentClass { }

  Object.keys(decorators).forEach(decoratorName => {
    decoratedClass.prototype[decoratorName] = function(...args) {
      decorators[decoratorName].call(this, ...args);
      return ponentClass.prototype[decoratorName].call(this, ...args)
    }
  })
  
  return decoratedClass
}

//
// Test 
//
class Component {
  foo() {
    console.log("foo")
  }
  
  bar() {
    console.log("bar")
  }
  
  baz() {
    console.log("baz")
  }
}

const DecoratedComponent = decorateMethods(Component, {
  foo() {
    console.log("before foo")
  },
  
  bar() {
    console.log("before bar")
  }
})

const d = new DecoratedComponent()
d.foo()
d.bar()
d.baz()

In this case the decorator methods exactly match the base class method names. If you want the decorator to use, e.g. beforeFoo instead, you could map method names with:

const methodName = decoratorName replace(/before(\w)/, (_, a) => a.toLowerCase())
发布评论

评论列表(0)

  1. 暂无评论