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} />
Related
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} />
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
I have just started to study ReactJS and have some questions. I was reading documentation here, but I can't find the answer I am looking for. Here is an example:
var Awesome = React.createClass({
getInitialState:function() {
return {
txt : ["1","2","3","4","5"],
isTrue : true
}
},
handleClick:function() {
this.setState({
isTrue : !this.state.isTrue
})
},
render:function() {
var changeStyle = {
display: this.state.isTrue ? "block" : "none"
};
var message = this.state.txt.map(function(oneMessage) {
return <SubChild change={changeStyle} txt={oneMessage}/>
});
return (
<div>
<button onClick={this.handleClick} >Click Me</button>
{message}
</div>
)
}
})
var SubChild = React.createClass({
render:function() {
return (
<div style={this.props.change}>
<h3>{this.props.txt}</h3>
</div>
)
}
})
React.render(<Awesome />, document.body)
Everything works fine, but I have some questions. As you can see I store my state inside a variable. Is this the best practice? How can I achieve the same result without variables inside render function or actually without states (I am trying to avoid state). Is this possible?
Here is my Fiddle
Why State Variables?
The idea of using state variables is to have changing / dynamic data, ie if anything about the component is changing, it should be defined as a state variable in the component so user interaction can result in change of this variable and a change in this variable causes the effected component to re-render.
Use of Properties
If some value is changed for each instance of the component and is uneffected by user interaction or component state change, it should be defined as a property so it can be assigned only once at instantiation.
In all cases, we cannot really avoid the use of variables inside the render
with React v0.12 the #jsx pragma is gone which means it is no longer possible to output jsx with anything other than the React.METHODNAME syntax.
For my use case I am trying to wrap the React object in another object to provide some convenience methods thus, in my component files, I want to be able to write:
var myConvenienceObject = require('React-Wrapper');
var Component = myConvenienceObject.createSpecializedClass({
render: function () {
return <div />
}
})
However the jsx compiler automatially converts <div /> into React.createElement("div", null)
With older versions of React it was possible to handle this using the pragma at the top of the file. However, since that has been removed, I was wondering if there was any way currently to change the name of the object compiled by jsx so <div /> would be transformed into myConvenienceObject.createElement("div", null)
No, it's no longer possible to use a custom prefix for JSX. If you need to do this, you'll need to modify the JSX transform code, or create a fake React.
var React = require('react'), FakeReact = Object.assign({}, React, {
createElement: function(component, props, ...children){
// ...
// eventually call the real one
return React.createElement(component, props, ...children);
}
});
module.exports = FakeReact;
And then to use it you import the fake react and call it React.
var React = require('fake-react');
// ...
render: function(){ return <div />; }
If you would like to make some elements contains in your myConvenienceObject, you could consider the children props as shown in the doc. But this may need some changes in the myConvenienceObject too, to accept the children.
By the way, i'm not sure where is this createSpecializedClass functions comes from and what it does
I asked this question previously, but with React 0.12 and the JSX changes it brought, I'm now seeking another way to implement this feature.
Here's the code that works in React 0.11:
return React.createClass({
render: function() {
var Tag = React.DOM[this.props.element];
return (
<Tag className='text'>
{this.props.value}
</Tag>
);
}
});
I'm making a component that renders different DOM elements based on the prop passed in called "element". this.props.element will be a value such as "p", "h1" or "h2" etc. While this technically works, I'm seeing a warning message in the console:
Warning: Do not pass React.DOM.p to JSX or createFactory. Use the string "p" instead.
Warning: Do not pass React.DOM.h2 to JSX or createFactory. Use the string "h2" instead.
Need some direction to help interpreting that warning and I can't find good documentation for how to do something like this.
Super simple answer to this (+1 for React 0.12 making things simpler!) but all that needed to be done is remove the React.DOM[] part of the variable definition, passing the string literal:
return React.createClass({
render: function() {
var Tag = this.props.element;
return (
<Tag className='text'>
{this.props.value}
</Tag>
);
}
});
Works beautifully; without any console warnings!