reactjs - How to solve this using composition instead of mixins in React? -
let's have dataset on car sales, , i'm building components display charts show different aspect of data.
one component example show average selling price of cars year, while 1 show how amount of cylinders relates mileage in cars bought last year.
these components additionally further parameterised, show data cars bought in given country, etc. appears using props pass both parameters , data seems appropriate, these components can used this:
<mileagebycylinder country="us" year="2014" dataset={data} /> <avgsellingpricesbyyear country="us" dataset={data} /> so far, good. let's i'm building 2 views (pages) compose these components differently. first 1 statically showing selling prices year different countries, while other 1 have ui choosing country , show mileage cylinder. both views need somehow dataset pass chart components. how can logic fetching dataset re-used?
with mixins, can done follows:
var cardatamixin = { componentdidmount: { // fetch car data , // call this.setstate({cardata: fetcheddata}), // once data has been (asynchronously) fetched } } var firstview = react.createclass({ mixins: [cardatamixin], render: function() { return ( <div> <avgsellingpricesbyyear country="us" dataset={this.state.cardata} /> <avgsellingpricesbyyear country="uk" dataset={this.state.cardata} /> <avgsellingpricesbyyear country="fi" dataset={this.state.cardata} /> </div> ) } }) var secondview = react.createclass({ mixins: [cardatamixin], handlenewcountry: function(country) { this.state{country: country} }, render: function() { return ( <div> <countrychooser onchange={this.handlenewcountry} /> <mileagebycylinder country="{country}" year="2014" dataset={this.state.cardata} /> <avgsellingpricesbyyear country="{country}" dataset={this.state.cardata} /> </div> ) } }) ok, nice. lot of people advising not use mixins, , composition should used instead. way solve using composition have come follows:
make root component passes state children props:
var carsalesroot = react.createclass({ componentdidmount: { // fetch car data , // call this.setstate({cardata: fetcheddata}), // once data has been (asynchronously) fetched } renderchildren: function () { return react.children.map(this.props.children, function (child) { return react.addons.clonewithprops(child, { cardata: this.state.cardata }) }.bind(this)) }, render: function() { return <div>{this.renderchildren()}</div> } });create view without mixin:
var firstview = react.createclass({ render: function() { return ( <div> <avgsellingpricesbyyear country="us" dataset={this.props.cardata} /> <avgsellingpricesbyyear country="uk" dataset={this.props.cardata} /> <avgsellingpricesbyyear country="fi" dataset={this.props.cardata} /> </div> ) } });create wrapper component wraps both root component , main view component:
var firstviewmain = react.createclass({ render: function() { return ( <carsalesroot> <firstview /> </carsalesroot> ) } });
this feels bit tricky, , makes data flows less explicit. feel basic , should solvable in clean way. missing obvious? or idiomatic , clean solution?
another option using composition create "higher-order component," analogous higher-order function in functional programming, wrapper slight variation on code presented.
define higher-order component:
var bindtocardata = function (component) { return react.createclass({ componentdidmount: { // fetch car data , // call this.setstate({cardata: fetcheddata}), // once data has been (asynchronously) fetched }, render: function() { return <component cardata={ this.state.cardata } /> } }); });then wrap component when define it.
var firstview = bindtocardata(react.createclass({ render: function() { return ( <div> <avgsellingpricesbyyear country="us" dataset={this.props.cardata} /> <avgsellingpricesbyyear country="uk" dataset={this.props.cardata} /> <avgsellingpricesbyyear country="fi" dataset={this.props.cardata} /> </div> ) } }));
this save writing component (number 3 in question), , ties in data flow logic directly in component needs data.
you can pass additional parameters bindtocardata function if need to.
Comments
Post a Comment