Essential React.js Interview Questions | Toptal®

[]

Modularity is – in effect – something partially done with intention while coding, and partially done when refactoring afterwards.

Let’s first paint a scenario which we’ll model using each method above. Imagine we have three React Components: onScrollable, Loadable, and Loggable.

  • an onScrollable Component will listen to the window.onscroll event, and use a logging mechanism to record it
  • a Loadable Component will not render until one or more async requests have finished loading, and will use a logging mechanism to record when this occurs
  • a Loggable Component provides a logging mechanism, be it a console, a Winston Node.js logging setup on our own server, or some 3rd party logging service that records logs via JSON requests

First, let’s model this with React’s ES5 API and mixins.

Interactive code sample at Matthew Keas’ github.

var onKeypress = { componentDidMount(){ this.onpress && window.addEventListener(‘keyup’, this.onpress) }, componentWillUnmount(){ this.onpress && window.removeEventListener(‘keyup’, this.onpress) } } var Loadable = { componentDidMount(){ if(this.load){ this.setState({loaded: false}) Promise.all([].concat(this.load)) .then(() => this.setState({loaded: true})) } } } var Loggable = { log(…args) { alert(args) } } var Example = React.createClass({ mixins: [Loggable, Loadable, onKeypress], componentWillMount(){ this.onpress = (e) => this.log(e.which, ‘pressed!’) this.load = [new Promise((res,rej) => setTimeout(res, 3000))] this.log = (…args) => console.log(…args) }, getInitialState(){ return {} }, render() { if(!this.state.loaded) return

loading…

return

test

} }) DOM.render(, document.body)

Let’s note a few things about the above code:

  1. There are three POJOs (Plain ol’ JS Objects) created, which hold lifecycle and/or custom methods.
  2. When creating the Example Component, we add mixins: [Loggable, Loadable, onKeypress], meaning that any functions from all three objects are included in the Example class.
  3. Both onKeypress and Loadable add a componentDidMount(), but this doesn’t mean the latter cancels out the prior. In fact, all componentDidMount() functions from each mixin will be invoked when the event occurs. The same is true for all lifecycle methods added to mixins. This way, both the onKeypress and Loadable mixins will work simultaneously!

Mixins are possible, but not built-in to React’s ES6 API. However, the ES6 API makes it easier to create a custom Component that extends another custom Component.

So our Components’ prototype chains would look like the following:

[Example] — extends —> [Loggable] — extends —> [Loadable] — extends —> [onKeypress]

This would result from Components written as such:

class onKeypress {} class Loadable extends onKeypress {} class Loggable extends Loadable {} class Example extends Loggable {}

Creating anonymous classes would help here, because then Loggable would not have to extend Loadable and onKeypress.

class Example extends (class a extends Loggable extends …) { }

With a mixin() function, this could look more like:

class Example extends mixin(Loggable, Loadable, onKeypress) { }

Let’s try to write mixin() by building a chain of anonymous classes that extend Loggable, Loadable, and onKeypress:

const mixin = (…classes) => classes.reduce((a,v) => { return (class temp extends a) }, (class temp {}))

There’s a caveat, though – if Loadable extends onKeypress and both implement componentDidMount(), Loadable’s version will be lower on the prototype chain, which means the function from onKeypress will never be invoked.

The takeaway here is that the mixin pattern isn’t easily implemented by relying only on the ES6 extends approach. Let’s try to implement mixin() again, but build a more robust function:

const mixin = (…classes) => { class _mixin {} let proto = _mixin.prototype classes.map(({prototype:p}) => { Object.getOwnPropertyNames(p).map(key => { let oldFn = proto[key] || (() => {}) proto[key] = (…args) => { oldFn(…args) return p[key](…args) } }) }) return _mixin }

This new mixin() implementation maps over each class, and cascades function calls from a parent class’s componentDidMount() alongside the child’s componentDidMount().

There are similar implementations of mixin() available on npm, using packages like react-mixin and es6-react-mixins.

We use mixin() from above like so:

interactive code sample available at https://goo.gl/VnQ21R

class A { componentDidMount(){ console.log(1) } } class B { componentDidMount(){ console.log(2) } } class C extends mixin(A,B) { componentDidMount(…p){ super.componentDidMount(…p) console.log(3) } } let c = new C() c.componentDidMount() // logs 1, 2, 3

Recently, React provided support for – and documented its preference of – React Components declared with ES6 classes. ES6 classes allow us to create component heirarchies with less code, however this makes it more difficult to create a single Component that inherits properties from several mixins, instead forcing us to create prototype chains.

Source

Leave a comment

Your email address will not be published. Required fields are marked *