javascript - instalar - ReactJs: crear un componente "If"... ¿una buena idea?
react js init (12)
He leído en los documentos React que las sentencias de tipo "if" no se pueden usar en código JSX, debido a la forma en que JSX lo convierte en javascript, no funciona como uno esperaría.
¿Pero hay alguna razón por la cual implementar un componente "si" es una mala idea? Parece que funciona bien desde mis pruebas iniciales, y me hace preguntarme por qué esto no se hace con más frecuencia.
Parte de mi intención es permitir que el desarrollo de reacción sea lo más posible, basado en marcado, con la menor cantidad de javascript posible. Este enfoque, para mí, se siente más como un enfoque "basado en datos".
Puedes verlo aquí en JS Fiddle
<script type=''text/javascript'' src="https://unpkg.com/[email protected]/dist/JSXTransformer.js"></script>
<script type=''text/javascript'' src="https://unpkg.com/[email protected]/dist/react-with-addons.js"></script>
<script type="text/jsx">
/** @jsx React.DOM */
var If = React.createClass({
displayName: ''If'',
render: function()
{
if (this.props.condition)
return <span>{this.props.children}</span>
return null;
}
});
var Main = React.createClass({
render: function() {
return (
<div>
<If condition={false}>
<div>Never showing false item</div>
</If>
<If condition={true}>
<div>Showing true item</div>
</If>
</div>
);
}
});
React.renderComponent(<Main/>, document.body);
</script>
Ejecutando los resultados anteriores en:
Mostrando artículo verdadero
Consulte la sección If-Else en JSX en los documentos de reacción.
En JSX, no puede poner declaraciones dentro de llaves, solo expresiones. Si no conoce la diferencia entre expresiones vs declaraciones en JavaScript, lea este article . Esta limitación se debe a que JSX desagua en llamadas a funciones y no puede usar sentencias if como argumentos para una función en JavaScript. Sin embargo, puede usar operadores booleanos ( &&
, ||
y ? :
&&
Para hacer un trabajo similar. Son expresiones para que quepan dentro de las llamadas de constructor que genera JSX y su evaluación de cortocircuito es la misma que la utilizada en las sentencias if.
<div>
{(true
? <div>Showing true item</div>
: <div>Never showing false item</div>
)}
</div>
<p>My name is {this.name || "default name"}</p>
Además, React tratará null
y false
como un "componente vacío" que no se procesa en el DOM real (actualmente utiliza el mismo truco de noscript detrás de las escenas). Esto es útil cuando no desea una rama "else". Ver False en JSX para más detalles.
<div>
{shouldIncludeChild ? <ChildComponent/> : false}
</div>
En cuanto al componente If sobre el que preguntaste, un problema es que en su forma actual evaluará sus hijos incluso si la condición es falsa. Esto puede conducir a errores cuando el cuerpo del If solo tiene sentido si la condición es verdadera:
<If condition={person !== null}>
//This code throws an exception if this.person is null
<div>{person.name}</div>
</If>
Podría solucionar esto haciendo que el componente if reciba el cuerpo como una función en lugar de como una lista de componentes secundarios, pero es más detallado:
<If condition={person !== null} body={function(){
return <div>{person.name}</div>
}/>
Finalmente, como el componente If no tiene estado, debería considerar usar una función simple en lugar de una nueva clase de componente, ya que esto haría que el "Si" sea transparente al algoritmo de reconciliación de React. Si usa un componente If, entonces un <div>
y un <If><div>
se considerarán incompatibles y React hará un redibujado completo en lugar de intentar fusionar el nuevo componente con el anterior.
// 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>
Creo que tu pregunta principal no es "¿Puedo implementar un componente If " (porque claramente puedes), sino "¿Hay alguna razón por la que implementar un componente If sea una mala idea?"
La razón principal por la que es una mala idea es porque agrega ciclos de procesamiento de componentes a lo que de otro modo sería una pequeña cantidad de código en un componente existente. El envoltorio adicional del componente significa que React necesita ejecutar un ciclo de renderizado adicional completo en sus hijos. Esto no es gran cosa si el componente If solo se usa en un lugar, pero si su aplicación está llena de ellos, acaba de hacer que su UI sea mucho más lenta.
Similar a cómo: <div>Hi</div>
es más eficiente que <div><div>Hi</div></div>
.
ES7 do
closure es ideal para eso:
Por ejemplo, si está utilizando un webpack
, puede agregar funciones de ES7:
Primero instala las características de la stage-0
con:
npm install babel-preset-stage-0 -save-dev;
Luego, en la configuración de los cargadores de webpack, agregue el preestablecimiento de babel stage-0:
loaders : [
//...
{
test : //.js$/,
loader : "babel-loader",
exclude : /node_modules/,
query : {
presets : ["react", "stage-0", "es2015"]
}
}
]
Entonces su función de renderizado de componentes puede verse así:
import React , { Component } from "react";
class ComponentWithIfs extends Component {
render() {
return (
<div className="my-element">
{
do {
if ( this.state.something ) {
<div>First Case</div>
} else {
<div>2nd Case</div>
}
}
}
</div>
);
}
}
Intentaré evitar el renderizado condicional con "plantillas" por completo. En React y JSX puede usar el conjunto completo de funciones de JavaScript para hacer que el renderizado condicional suceda:
- if-else
- ternario
- lógico &&
- caja del interruptor
- enums
Eso es lo que hace que el renderizado con JSX sea tan poderoso. De lo contrario, uno podría inventar de nuevo lenguajes de plantillas propios que sean específicos de la biblioteca / marco, como en Angular 1/2.
La sintaxis experimental ES7 do
es realmente agradable para esta situación. Si usa Babel, active la función es7.doExpressions
luego:
render() {
var condition = false;
return (
<div>
{do {
if (condition) {
<span>Truthy!</span>;
} else {
<span>Not truthy.</span>;
}
}}
</div>
);
}
Ver http://wiki.ecmascript.org/doku.php?id=strawman:do_expressions
No necesitas nada más que simple JS.
Seguro y legible (pero prolijo)
maybeRenderPerson: function() {
var personName = ...;
if ( personName ) {
return <div className="person">{personName}</div>;
}
}
render: function() {
return (
<div className="component">
{this.maybeRenderPerson()}
</div>
);
}
Es un poco detallado, pero permite dividir fácilmente su lógica en bloques más pequeños y enfocados. Cuando los componentes comienzan a ser complejos, este es el más legible.
Conciso y legible (pero peligroso)
render: function() {
var personName = ...; // present or absent name, but never ""
return (
<div className="component">
{personName && (
<div className="person">{personName}</div>
)}
</div>
);
}
Esta sintaxis puede ser bastante peligrosa si la variable probada puede ser valores falsy como 0
, ""
o false
. Particularmente con números, debería modificar ligeramente la prueba si quiere asegurarse de que rinde para 0:
render: function() {
var counter= ...; // number, can be 0
return (
<div className="component">
{(typeof counter !== ''undefined'') && (
<div className="counter">{counter}</div>
)}
</div>
);
}
Cuando los componentes se vuelven complejos, dividirse en múltiples componentes más pequeños y tener convenciones de estilo de código pueden ayudar a mantenerlo legible:
render: function() {
var person= ...;
var counter= ...;
return (
<div className="component">
{person && (
<Person person={person}/>
)}
{(typeof counter !== ''undefined'') && (
<Counter value={counter}/>
)}
</div>
);
}
Sintaxis moderna (pero demasiado pronto)
La notación do
con componentes funcionales sin estado puede ser útil para la expresividad sin perder legibilidad. Puede dividir fácilmente un componente grande en uno muy pequeño y centrado que utiliza la notación do
:
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}/>
}
Es un poco como usar el patrón de módulo dentro de JSX, por lo que es bastante flexible, pero con mucho menos repetitivo.
Para habilitar esto, necesita herramientas de compilación ES7 etapa 0, y el soporte de su IDE favorito podría no existir todavía.
Para el código de mantenimiento, omita abstracciones innecesarias como If
y agregue alguna lógica de control con una terminación anticipada antes de la declaración de return
en su método de render
, por ejemplo
import React from ''react-native'';
let {
Text
} = React;
let Main = React.createClass({
setInitialState() {
return { isShown: false }
},
render() {
let content;
if (this.state.isShown) {
content = ''Showing true item''
} else {
return false;
}
return (
<Text>{content}</Text>
);
}
});
Te mantiene SECO. Protección duradera
Para mí es suficiente aprovechar la evaluación perezosa en una expresión lógica si no se necesita ninguna otra declaración:
render() {
return(
<div>
{
condition &&
<span>Displayed if condition is true</span>
}
</div>
);
}
O el operador de ternay si se necesita otro:
render() {
return(
<div>
{
condition ?
<span>Displayed if condition is true</span>
:
<span>Displayed if condition is false</span>
}
</div>
);
}
Puedes hacer condicionales en línea como este
{true && (
<div>render item</div>
)}
{false && (
<div>don''t render item</div>
)}
O simplemente puedes usar una var
var something;
if (true) {
something = <div>render item</div>
}
{something}
Puedes hacer una función autoejecutable, con el contexto correcto también (¡aunque con la ayuda de Babel)! Lo cual prefiero porque no hay necesidad de asignar variables y puede ser tan complejo como desee (aunque probablemente no lo haga por el mantenimiento):
render() {
return (
<div>
<div>Hello!</div>
{() => {
if (this.props.isLoggedIn) {
return <Dashboard />
} else {
return <SignIn />
}
}()}
<div>Another Tag</div>
</div>
);
}
Si alguien está interesado, acabo de lanzar un módulo de reacción para estos fines:
var Node = require(''react-if-comp'');
...
React.createClass({
render: function() {
return (
<div>
<Node if={false} else={<div>Never showing false item</div>} />
<Node if={true} then={<div>Showing true item</div>} />
</div>
);
}
});
render-if es una función ligera que representará un elemento si se cumple una condición
Como una expresión en línea
class MyComponent extends Component {
render() {
return (
{renderIf(1 + 2 === 3)(
<span>The universe is working</span>
)}
);
}
}
Como una función nombrada
class MyComponent extends Component {
render() {
const ifTheUniverseIsWorking = renderIf(1 + 2 === 3);
return (
{ifTheUniverseIsWorking(
<span>The universe is still wroking</span>
)}
)
}
}
Como una función compuesta
const ifEven = number => renderIf(number % 2 === 0);
const ifOdd = number => renderIf(number % 2 !== 0);
class MyComponent extends Component {
render() {
return (
{ifEven(this.props.count)(
<span>{this.props.count} is even</span>
)}
{ifOdd(this.props.count)(
<span>{this.props.count} is odd</span>
)}
);
}
}