ReactJs - Creating an "If" component... a good idea?
Check out the If-Else in JSX section in the react Docs.
In JSX, you can't put statements inside curly braces--only expressions. If you don't know the difference between expressions vs statements in JavaScript read this article. This limitation is because JSX desugars into function calls and you can't use if-statements as arguments to a function in JavaScript. However, you can use boolean operators (&&
, ||
and ? :
) to do a similar job. They are expressions so they can fit inside the constructor calls JSX generates and their short-circuiting evaluation is the same as the one used in if statements.
<div>
{(true
? <div>Showing true item</div>
: <div>Never showing false item</div>
)}
</div>
<p>My name is {this.name || "default name"}</p>
Additionally, React will treat null
and false
as an "empty component" that does not get rendered in the real DOM (currently it uses that same noscript trick behind the scenes). This is useful when you don't want an "else" branch. See False in JSX for details.
<div>
{shouldIncludeChild ? <ChildComponent/> : false}
</div>
As for the If component you asked about, one problem it has is that in its current form it will evaluate its children even if the condition is false. This can lead to errors when the body of the If only makes sense if the condition is true:
<If condition={person !== null}>
//This code throws an exception if this.person is null
<div>{person.name}</div>
</If>
You could workaround this by having the if component receive the body as a function instead of as a list of child components but its more verbose:
<If condition={person !== null} body={function(){
return <div>{person.name}</div>
}/>
Finally, since the If component is stateless, you should consider using a plain function instead of a new component class, as this would make the "If" transparent to React's reconciliation algorithm. If you use an If component, then a <div>
and a <If><div>
will be considered incompatible and React will do a full redraw instead of trying to merge the new component with the old one.
// This custom if function is for purely illustrative purposes
// However, this idea of using callbacks to represent block of code
// is useful for defining your own control flow operators in other circumstances.
function myCustomIf(condition, onTrue, onFalse){
onTrue = onTrue || function(){ return null }
onFalse = onFalse || function(){ return null }
if(condition){
return onTrue();
}else{
return onFalse();
}
}
<div>
{myCustomIf(person !== null, function(){
return <div>{person.name}</div>
})}
</div>
You can do inline conditionals like this
{true && (
<div>render item</div>
)}
{false && (
<div>don't render item</div>
)}
Or you can just use a var
var something;
if (true) {
something = <div>render item</div>
}
{something}
You don't need anything than plain JS.
Safe and readable (but verbose)
maybeRenderPerson: function() {
var personName = ...;
if ( personName ) {
return <div className="person">{personName}</div>;
}
}
render: function() {
return (
<div className="component">
{this.maybeRenderPerson()}
</div>
);
}
It is a bit verbose, but it permits to easily split your logic in smaller, focused blocks. When components start to become complex, this is the most readable.
Concise and readable (but dangerous)
render: function() {
var personName = ...; // present or absent name, but never ""
return (
<div className="component">
{personName && (
<div className="person">{personName}</div>
)}
</div>
);
}
This syntax can be quite dangerous if the tested variable can be falsy values like 0
,""
or false
. Particularly with numbers you should rather modify the test slightly if you want to make sure it renders for 0:
render: function() {
var counter= ...; // number, can be 0
return (
<div className="component">
{(typeof counter !== 'undefined') && (
<div className="counter">{counter}</div>
)}
</div>
);
}
When components become complex, splitting into multiple smaller components, and having code-style conventions can help to keep it readable:
render: function() {
var person= ...;
var counter= ...;
return (
<div className="component">
{person && (
<Person person={person}/>
)}
{(typeof counter !== 'undefined') && (
<Counter value={counter}/>
)}
</div>
);
}
Modern syntax (but too early)
The do
notation with functional stateless components can be useful for expressiveness without loosing readability. You can easily split a large component into very small and focused ones that use the do
notation:
const Users = ({users}) => (
<div>
{users.map(user =>
<User key={user.id} user={user}/>
)}
</div>
)
const UserList = ({users}) => do {
if (!users) <div>Loading</div>
else if (!users.length) <div>Empty</div>
else <Users users={users}/>
}
It is a bit like using the module pattern inside JSX, so it's quite flexible, but with a lot less boilerplate.
To enable this, you need ES7 stage 0 compilation tooling, and support from your favorite IDE might not exist yet.