javascript - usar - Uso correcto de las funciones de flecha en React.
sintaxis funcion flecha (5)
Estoy usando ReactJS con Babel y Webpack y uso ES6, así como los campos de clase propuestos para las funciones de flecha. Entiendo que las funciones de flecha hacen que las cosas sean más eficientes al no volver a crear las funciones, cada una de ellas es similar a cómo funciona el enlace en el constructor. Sin embargo, no estoy 100% seguro de si los estoy usando correctamente. La siguiente es una sección simplificada de mi código en tres archivos diferentes.
Mi código:
Main.js
prevItem = () => {
console.log("Div is clicked")
}
render(){
return (
<SecondClass prevItem={this.prevItem} />
)
}
SecondClass.js
<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />
ThirdClass.js
<div onClick={()=>{this.props.onClick()}}>Previous</div>
Pregunta:
¿Mi código de arriba está usando las funciones de flecha correctamente? Noté que para SecondClass.js también podría haber usado:
<ThirdClass type="prev" onClick={this.props.prevItem} />
¿Hay alguna diferencia entre un método u otro ya que usé una función de flecha ES6 en mi definición de función original? ¿O debería estar usando la sintaxis de las flechas hasta mi última división?
Entiendo que las funciones de flecha hacen que las cosas sean más eficientes al no recrear las funciones cada vez que son referidas
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… .
Las funciones de flecha manejan this
contexto de una manera léxica, donde la función "normal" lo hace dynamically .
En ambos ejemplos de la función de flecha en línea, está creando una nueva instancia de función en cada render
.
Esto creará y pasará una nueva instancia en cada render
onClick={() => {}}
En el tercer ejemplo solo tienes una instancia.
Esto solo pasa una referencia a una instancia ya existente
onClick={this.myHandler}
En cuanto a los beneficios de las funciones de flecha como campos de clase (hay un pequeño inconveniente , lo publicaré en la parte inferior de la respuesta), si tiene un controlador de función normal que necesita acceder a la instancia actual de la class
través de this
:
myHandler(){
// this.setState(...)
}
Tendrá que bind
explícitamente a la class
.
El enfoque más común será hacerlo en el constructor
porque se ejecuta solo una vez:
constructor(props){
super(props);
this.myHandler = this.myHandler.bind(this);
}
Sin embargo, si usa una función de flecha como controlador, no necesita bind
a la class
porque, como se mencionó anteriormente, la función de flecha usa un contexto léxico para this
:
myHandler = () => {
// this.setState(...)
}
Con ambos enfoques usarás el controlador de esta manera:
<div onClick={this.myHandler}></div>
La principal razón para adoptar este enfoque:
<div onClick={() => this.myHandler(someParameter)}></div>
Es si desea pasar parámetros al controlador junto al event
nativo que se pasa, lo que significa que desea pasar un parámetro hacia arriba.
Como se mencionó, esto creará una nueva instancia de función en cada render.
(Hay un mejor enfoque para esto, sigue leyendo).
Ejecución de ejemplo para tal caso de uso:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [{ name: ''item 1'', active: false }, { name: ''item 2'', active: true }],
}
}
toggleITem = (itemName) => {
this.setState(prev => {
const nextState = prev.items.map(item => {
if (item.name !== itemName) return item;
return {
...item,
active: !item.active
}
});
return { items: nextState };
});
}
render() {
const { items } = this.state;
return (
<div>
{
items.map(item => {
const style = { color: item.active ? ''green'' : ''red'' };
return (
<div
onClick={() => this.toggleITem(item.name)}
style={style}
>
{item.name}
</div>
)})
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById(''root''));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Un mejor enfoque sería crear una composición de componentes.
Puede crear un componente secundario que envuelva el marcado relevante, tendrá su propio controlador y obtendrá tanto los data
como el handler
como accesorios del padre.
El componente hijo invocará entonces el controlador que obtuvo del padre y pasará los data
como un parámetro.
Ejecución de ejemplo con componente hijo:
class Item extends React.Component {
onClick = () => {
const { onClick, name } = this.props;
onClick(name);
}
render() {
const { name, active } = this.props;
const style = { color: active ? ''green'' : ''red'' };
return (<div style={style} onClick={this.onClick}>{name}</div>)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [{ name: ''item 1'', active: false }, { name: ''item 2'', active: true }],
}
}
toggleITem = (itemName) => {
this.setState(prev => {
const nextState = prev.items.map(item => {
if (item.name !== itemName) return item;
return {
...item,
active: !item.active
}
});
return { items: nextState };
});
}
render() {
const { items } = this.state;
return (
<div>
{
items.map(item => {
return <Item {...item} onClick={this.toggleITem} />
})
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById(''root''));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Campos de clase el lado negativo :
Como mencioné, hay un pequeño inconveniente para los campos de clase.
La diferencia entre un método de clase y un campo de clase es que el campo de clase se adjunta a la instance
de la class
(función de constructor).
donde a medida se adjuntan los métodos y objetos de clase al prototipo.
Por lo tanto, si tiene una cantidad ridículamente grande de instancias de esta clase, puede obtener un impacto en el rendimiento.
Dado este bloque de código:
class MyClass {
myMethod(){}
myOtherMethod = () => {}
}
Babel lo transpilará a esto:
var _createClass = function() {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function(Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var MyClass = function() {
function MyClass() {
_classCallCheck(this, MyClass);
this.myOtherMethod = function() {};
}
_createClass(MyClass, [{
key: "myMethod",
value: function myMethod() {}
}]);
return MyClass;
}();
Entiendo que las funciones de flecha hacen que las cosas sean más eficientes al no volver a crear las funciones, cada una de ellas es similar a cómo funciona el enlace en el constructor.
Esto no es verdad. Depende de dónde está utilizando exactamente la función de flecha. Si se utiliza la Arrow function
en el método de representación, se creará una nueva instancia everytime
que se llame a la representación, de la misma forma en que funcionaría el bind
. Considera este ejemplo
<div onClick={()=>{this.onClick()}}>Previous</div>
Aquí, cada vez que se llama render, se crea una función anónima y esa función cuando se llama, lo llama.
Sin embargo consideremos el caso de abajo.
onClick = () => {
console.log("Div is clicked")
}
En el caso anterior, la función de flecha no vuelve a crear la función cada vez, sino que vincula el contexto al componente Reaccionar, ya que An arrow function does not have its own this; the this value of the enclosing execution context is used.
An arrow function does not have its own this; the this value of the enclosing execution context is used.
una vez cuando la clase es instanciada. Esto es similar a cómo binding works is constructor
. Esta es una parte de proposed class fields for arrow functions
y no es una función de ES6,
Para comprender lo que desea preguntar, debe saber que una función obtiene su contexto desde donde se llama. Compruebe this question
para una mayor comprensión.
En su caso, ha utilizado la Arrow function
para definir prevItem
y, por lo tanto, obtiene el contexto del componente React que lo contiene.
prevItem = () => {
console.log("Div is clicked")
}
render(){
return (
<SecondClass prevItem={this.prevItem} />
)
}
Ahora en su hijo, incluso si llama prevItem
con cualquier contexto personalizado, using bind or arrow function
, prevItem
cuando se ejecuta en parent, es decir, Main.js
obtendrá el contexto de su componente React adjunto. Y ya que solo desea ejecutar la función prevItem y no quiere pasarle ningún dato a este desde el niño, escriba
<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />
y
<div onClick={()=>{this.props.onClick()}}>Previous</div>
es simplemente inútil y solo se agregará a la implicación de rendimiento ya que las nuevas funciones se crean en SecondClass
y ThirdClass
cada vez. Simplemente no necesita tener estas funciones definidas como funciones de flecha y podría simplemente escribir
<ThirdClass type="prev" onClick={this.props.prevItem} />
y
<div onClick={this.props.onClick}>Previous</div>
ya que está ya enlazado en el padre.
Ahora, incluso si tiene que pasar algunos datos adicionales a estas funciones desde ThirdClass y SecondClass, no debe usar directamente la Arrow function
o bind in render
. Eche un vistazo a esta respuesta en How to Avoid binding in Render method
Así que tu primer acercamiento
<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />
En esto, puede pasar cualquier argumento que esté disponible en ThirdClass a la función prevItem. Es la buena manera de llamar a las funciones de los padres con argumentos.
<ThirdClass type="prev" onClick={()=>this.props.prevItem(firstArgument, secondArgument)} />
Su segundo enfoque es
<ThirdClass type="prev" onClick={this.props.prevItem} />
Este enfoque no le permite pasar ningún argumento específico de ThirdClass.
Los dos apporaches tienen razón, solo eso, depende de su caso de uso. Ambos enfoques utilizan la función de flecha de es6 y tienen razón en los escenarios respectivos mencionados anteriormente
El uso de flechas en la definición de su función original le permite no vincular la función en su constructor.
Si no usaste una flecha ...
prevItem(){
console.log("Div is clicked")
}
Entonces tendrías que crear un constructor y enlazarlo allí ...
class MyComponent extends Component {
constructor(props) {
super(props)
this.prevItem = this.prevItem.bind(this)
}
prevItem() { ... }
}
Usar la flecha es más fácil cuando empiezas, ya que simplemente funciona y no tienes que entender qué es un constructor y profundizar en las complejidades de this
en JavaScript.
Sin embargo, el rendimiento es mejor enlazar en el constructor. El método de vinculación en constructor creará una instancia única de la función y la reutilizará, incluso si el método de renderización se llama varias veces.
Usar el cierre de JavaScript
puede ser una forma diferente de otras respuestas, preste atención a los siguientes códigos:
clickHandler = someData => e => this.setState({
stateKey: someData
});
Ahora en JSX
, puedes escribir:
<div onClick={this.clickHandler(''someData'')} />
someData
with someData
devuelve una función con e
argumento e
pero no se usa dentro de la función clickHandler
. por lo que funciona bien
Para escribir más completamente escribe como a continuación:
clickHandler = someData => () => this.setState({
stateKey: someData
});
No es necesario e
, entonces por qué debería escribirlo.