I have the following component today, which represents a container box that has a heading and different rows. This is how it looks:
var Box = React.createClass({
render: function() {
return (
<BoxHeading title={this.props.headingTitle}/>
<BoxBody rows={this.props.rows} />
)
}
});
var BoxBody = React.createClass({
render: function() {
return (
{this.rows()}
)
}
rows: function() {
return _.map(this.props.rows, function(row) {
return (
<HouseRow />
);
}, this);
}
});
Now my question is: If I want to reuse the Box & BoxBody but instead of using I want to use another kind of Row, how would I do it?
Would I pass the kind of component that I want as row to the Box?
So, I would do <Box rowType={<HouseRow} /> or something similar?
The approach you chose looks really good — you can basically combine Box with every type of row you want, provided it has a correct interface. It is called Object Composition and is a legit and well respected pattern in software engineering.
The only thing is, in React you should do it not by passing a already rendered component, but the component class, like this:
<Box rowComponent={ HouseRow } />
As you see, you don't need to use parenthesis. And inside component, you do something like this:
var RowComponent = this.props.rowComponent;
return (
<RowComponent />
);
<Box rowType={HouseRow} />
This is exactly how you would do it. Pass dynamic content that varies from components as props from a parent. Then in your rows function, return <{#props.rowType} /> should work
Related
As the headline states: is something like the pseudo code below considered bad?
<Outer
a = { ComponentA }
b = { ComponentB }
/>
var Outer = (props) => {
var ComponentA = props.a;
var ComponentB = props.b;
// do fancy stuff
// ...
return (
<div>
<ComponentA { ...fancypropsForA } />
<ComponentB { ...fancypropsForB } />
</div>
);
}
As an example: I'm using it to display tree data in different ways by passing a component that will render the data of a single node.
EDIT
As requested, I will try to make my question a little more clear.
There is a component that has some logic and some markup that is the same every time you use this component. But there are two (or more) places in that markup that should be replacable.
Picture a calendar that displays a whole month. There is a component that renders an individual date, and one that renders the weekday (in the bar at the top).
You want to reuse that calendar in multiple places, but you need different markup for the date/weekday components each time.
One way to achieve this is:
<Calendar
data={ data }
weekdayComponent={ MyWeekDayComponent }
dateComponent={ MyDateComponent }
/>
<Calendar
data={ data }
weekdayComponent={ SomeOtherWeekDayComponent }
dateComponent={ SomeOtherDateComponent }
/>
So, i found that this works. But I'm not sure if that is actually bad.
As long as data flows only in one direction, you're generally OK. Your example is a little contrived, so it's hard to see what general problem you're trying to solve, but I think what you're actually looking for are Higher Order Components.
https://medium.com/#franleplant/react-higher-order-components-in-depth-cf9032ee6c3e
I would like to create a state object that combines both the state value itself and the ability to call setState whenever its value is changed. This way, e.g. on the interface between the container and the dumb component I won't have to pass a separate setter for each state property.
E.g. typically what gets done in the 'smart and dumb' pattern is the following:
let TextValueContainer = React.createClass({
getInitialState: function () {
return {value: ''};
},
setValue(v) {
this.setState({value: v});
},
render: function() {
return (
<TextValue
value={this.state.value}
setValue={this.setValue}
/>
);
}
});
let TextValue = React.createClass({
propTypes: {
value: React.PropTypes.string.isRequired,
setValue: React.PropTypes.func.isRequired
},
render: function() {
return (
<input type={'text'}
onChange={function (ev) {
this.props.setValue(ev.target.value);
}.bind(this)}
value={this.props.value}
>
</input>
);
}
});
ReactDOM.render(<TextValueContainer/>, $('#app')[0]);
Observe how at the interface between the container and the dumb component two properties are passed: the state value itself (value) and a method to change it (setValue). For N separate state attributes I would need 2*N props to be passed at the interface. Plus, there is no hard way looking at the code of the dumb component to figure out which setter is for which value.
I experimented a bit and come up with the following:
class StateHolder {
constructor(v, that) {
this.v = v;
this.setState = function(v2) {
this.setState(Object.assign({}
,this.state
,{valueHolder: new StateHolder(v2, that)}));
}.bind(that);
}
}
let TextValueContainer = React.createClass({
getInitialState: function () {
return {valueHolder: new StateHolder('', this)};
},
render: function() {
return (
<TextValue
valueHolder={this.state.valueHolder}
/>
);
}
});
let TextValue = React.createClass({
propTypes: {
valueHolder: React.PropTypes.instanceOf(StateHolder).isRequired
},
render: function() {
return (
<input type={'text'}
onChange={function (ev) {
this.props.valueHolder.setState(ev.target.value);
}.bind(this)}
value={this.props.valueHolder.v}
>
</input>
);
}
});
ReactDOM.render(<TextValueContainer/>, $('#app')[0]);
In the above implementation for each attribute, only a single props needs to be passed between the 'smart' and the 'dumb' component (in the example above valueHolder).
My questions are:
is there a simpler way to accomplish what I am trying to do? (i.e. simplify the interface between the 'smart' and the 'dumb' component and make explicit the association between the values passed down and their setter functions)
do you see any anti-patterns or code smells in the "solution" given above?
Looks like you'd want to take a look at some framework to manage your flow of data and the corresponding changes in the app state. I've been working with Redux a lot recently and I would recommend it. It's a very smart implementation of the Flux architecture. Your "stateHolder" concept is resolved in the Redux store.
From the Redux motivation page
Following in the steps of Flux, CQRS, and Event Sourcing, Redux
attempts to make state mutations predictable by imposing certain
restrictions on how and when updates can happen. These restrictions
are reflected in the three principles of Redux.
Of course, there are alternatives to Redux; the point is that what you're trying to do would become really hard to maintain and understand and that you should look at a generalised way to manage your state.
Tell me what do you think about this JSBin: http://jsbin.com/tofepoliha/edit?js,output
First, when declaring the TextValue element, instead of using value={this.state.value} value2={this.state.value2}, we pass the whole state in one action using the spread (...) operator.
<TextValue {...this.state} setValue={this.setValue} />
This way we don't need to repeat ourselves for each and every property. Now, for the setValue function - instead of having a special function for every property, we can simply declare one generic function, that gets the key and the value and sets it to the parent component's state:
setValue(value, v) {
this.setState({[value]: v});
}
Then, we can have as many inputs (or every other element for this matter) like so:
<input type="text" onChange={function (ev) {
this.props.setValue('value', ev.target.value);
}.bind(this)} value={this.props.value} />
<input type="text" onChange={function (ev) {
this.props.setValue('value2', ev.target.value);
}.bind(this)} value={this.props.value2} />
Is there a way of composition of dynamic component? For example, lets say I would a have a ListItems component that display its children in rows. But i want to reuse some of its behavior and I would like to to something like that:
<ListItems rowComponent="CarsItem" headerComponent="CarsHeader"/>
or
<ListItems rowComponent="BikesItem" headerComponent="BikesHeader"/>
How can I achieve such abstraction?
Somethin like that did not work
render:function(){
return (
<this.props.header/>
<this.props.body/>
)
}
If you can make sure you declare the class that's used for the children first, you can do something like this:
<ListItems rowComponent={BikesItem}/>
and then in your render method:
render() {
var Row = this.props.rowComponent;
return (
<div>
{this.props.items.map(function(item) {
return <Row item={item}/>;
})}
</div>
)
}
Hope this helps!
Say I want to create a list of UI elements, and I want each element to align differently. I was imagining something like:
<List>
<Button Alignment="right"/>
<Panel Alignment="left"/>
<Video Alignment="center"/>
</List>
How is this best achievable? Preferably by not adding special handling of the alignment property to each child element.
There are a number of ways to accomplish something like that in React, with no particular way necessarily being the "best."
I'd definitely recommend looking at:
http://facebook.github.io/react/docs/getting-started.html
Here's one idea using a simple mixin.
var List = React.createClass({
render: function() {
return (
<div>
<Button alignment="right" />
<Panel alignment="left" />
<Video alignment="center" />
</div>
);
}
});
var Button = React.createClass({
mixins: [AlignmentMixin],
render: function() {
return (
<Button { ... this.props } className={ this.renderAlignment() } />
);
}
});
var AlignmentMixin = {
renderAlignment: {
// whatever you'd like, just an example
// of constructing a class name called box-right/left/center
return "box-" + this.props.alignment;
}
}
There isn't an equivalent to an Angular directive that could be applied globally to all types. React takes a different approach and by design (currently) expects that components present a well-known interface and cannot be arbitrarily extended by the consumer of the component. It means that a component in ReactJS is only what it says it is and can do, and a parent component can only affect its behavior by setting properties on the component instance.
In the example I provided above, I've explicitly added the "alignment" mixin to the Button. Without it, the button would not have the feature of alignment.
Of course, you could just add the necessary code to the Button to do the alignment if there wasn't a general pattern that could be applied as simply as I have shown above.
Some might not even use a mixin as they would rather have the code that "is a" Button be only in a file called, Button.js (for example). The UI and behavior is not spread around multiple files potentially as it might be in an angular app (where directives may be in different files and difficult to discover what might be applied to any given element).
In fact, from the "why react page":
Since they're so encapsulated, components make code reuse, testing,
and separation of concerns easy.
I have a use case where I have an Image component that has a required "src" attribute and an optional "link" attribute which looks like this:
var Image = React.createClass({
propTypes: {
link: React.PropTypes.string,
event: React.PropTypes.object,
src: React.PropTypes.string.isRequired
},
handleClick: function(event, link) {
analytics.track(event)
.then(function() {
window.location = link;
});
},
render: function() {
return (
<img className='image' src={this.props.src} onClick={this.handleClick.bind(this, this.props.event, this.props.link)} />
);
} });
If I want to selectively include optional props when I call the Image component, how would I do that elegantly? My initial idea was to do a ternary expression like this, except this is not valid JSX:
render: function() {
return (
<Image src={this.props.src} {this.props.link.hasOwnProperty('value') ? link=this.props.link.value : ''} />
)
}
In the example above "this.props.link" is an object that may or may not contain a property called "value" which includes the hyperlink to browse to when the Image is clicked. Also, rather than simply supplying an empty string as the value for the "link" prop, I would prefer to simply leave it out altogether if there is no link.value present.
My reasoning for this is so that on the Image component I can add the css "img:hover {cursor: pointer;}" only if the img actually links somewhere, as opposed to setting it globally which violates UX rules for my app.
I know that I can simply render the "link" prop inside a ternary where it includes the value of the link if it exists and is an empty string if it doesn't, but for curiousity's sake I wanted to see if there maybe was another way to accomplish this.
I also would like to avoid having to do a bunch of conditional statements that create a lot of redundant JSX code like this:
render: function() {
if (this.props.link.hasOwnProperty('value')) {
return <Image link={this.props.link.value} src={this.props.src.value} />;
} else {
return <Image src={this.props.src.value} />;
}
.... // other optional properties
}
Imagine how out of hand that would get if you have a lot of optional props that you want to leave off...
You seem to be overthinking it.
<Image src={this.props.src} link={this.props.link.value} />
In your components you should usually treat any falsy value as omitted.
if (this.props.link) {
...
}
An exception would be for numbers, or the rare (and best avoided case) where it's a boolean defaulting to true.
A more direct answer would be to use spread (new in 0.12).
var props = {src: this.props.src};
if (this.props.link.hasOwnProperty('value')) {
props.link = this.props.link.value;
}
<Image {...props} />
or
var extraProps = {};
if (this.props.link.hasOwnProperty('value')) {
extraProps.link = this.props.link.value;
}
<Image src={this.props.src} {...extraProps} />