He estado leyendo un montón de react
código y veo cosas como esta que no entiendo:
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
He estado leyendo un montón de react
código y veo cosas como esta que no entiendo:
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
Respuestas:
Esa es una función curry
Primero, examine esta función con dos parámetros ...
const add = (x, y) => x + y
add(2, 3) //=> 5
Aquí está de nuevo en forma de curry ...
const add = x => y => x + y
Aquí está el mismo código 1 sin funciones de flecha ...
const add = function (x) {
return function (y) {
return x + y
}
}
Concentrarse en return
Puede ser útil visualizarlo de otra manera. Sabemos que las funciones de flecha funcionan así: prestemos especial atención al valor de retorno .
const f = someParam => returnValue
Entonces, nuestra add
función devuelve una función : podemos usar paréntesis para mayor claridad. El texto en negrita es el valor de retorno de nuestra función.add
const add = x => (y => x + y)
En otras palabras, add
algún número devuelve una función
add(2) // returns (y => 2 + y)
Llamar a funciones curry
Entonces, para usar nuestra función de curry, tenemos que llamarlo un poco diferente ...
add(2)(3) // returns 5
Esto se debe a que la primera llamada a la función (externa) devuelve una segunda función (interna). Solo después de llamar a la segunda función, realmente obtenemos el resultado. Esto es más evidente si separamos las llamadas en dos líneas ...
const add2 = add(2) // returns function(y) { return 2 + y }
add2(3) // returns 5
Aplicando nuestra nueva comprensión a su código
relacionado: "¿Cuál es la diferencia entre encuadernación, aplicación parcial y curry?"
Bien, ahora que entendemos cómo funciona, veamos tu código
handleChange = field => e => {
e.preventDefault()
/// Do something here
}
Comenzaremos por representarlo sin usar las funciones de flecha ...
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
};
};
Sin embargo, debido a que las funciones de flecha se unen léxicamente this
, en realidad se vería más así ...
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
}.bind(this)
}.bind(this)
Quizás ahora podamos ver lo que esto está haciendo más claramente. La handleChange
función está creando una función para un determinado field
. Esta es una técnica práctica de React porque debes configurar tus propios oyentes en cada entrada para actualizar el estado de tus aplicaciones. Al usar la handleChange
función, podemos eliminar todo el código duplicado que resultaría en la configuración de change
escuchas para cada campo. ¡Frio!
1 Aquí no tuve que vincular léxicamente this
porque la add
función original no usa ningún contexto, por lo que no es importante preservarla en este caso.
Aún más flechas
Se pueden secuenciar más de dos funciones de flecha, si es necesario:
const three = a => b => c =>
a + b + c
const four = a => b => c => d =>
a + b + c + d
three (1) (2) (3) // 6
four (1) (2) (3) (4) // 10
Las funciones curry son capaces de sorprender. A continuación vemos $
definido como una función currificada con dos parámetros, pero en el sitio de la llamada, parece que podemos suministrar cualquier número de argumentos. El curry es la abstracción de la aridad .
const $ = x => k =>
$ (k (x))
const add = x => y =>
x + y
const mult = x => y =>
x * y
$ (1) // 1
(add (2)) // + 2 = 3
(mult (6)) // * 6 = 18
(console.log) // 18
$ (7) // 7
(add (1)) // + 1 = 8
(mult (8)) // * 8 = 64
(mult (2)) // * 2 = 128
(mult (2)) // * 2 = 256
(console.log) // 256
Aplicación parcial
La aplicación parcial es un concepto relacionado. Nos permite aplicar parcialmente las funciones, de forma similar al currículum, excepto que la función no tiene que definirse en forma de currículum.
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const add3 = (x, y, z) =>
x + y + z
partial (add3) (1, 2, 3) // 6
partial (add3, 1) (2, 3) // 6
partial (add3, 1, 2) (3) // 6
partial (add3, 1, 2, 3) () // 6
partial (add3, 1, 1, 1, 1) (1, 1, 1, 1, 1) // 3
Aquí hay una demostración funcional con la partial
que puedes jugar en tu propio navegador:
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const preventDefault = (f, event) =>
( event .preventDefault ()
, f (event)
)
const logKeypress = event =>
console .log (event.which)
document
.querySelector ('input[name=foo]')
.addEventListener ('keydown', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">
$
se usó para demostrar el concepto, pero puedes nombrarlo como quieras. Casualmente pero completamente sin relación, $
se ha utilizado en bibliotecas populares como jQuery, donde $
es una especie de punto de entrada global a toda la biblioteca de funciones. Creo que también se ha usado en otros. Otro que verá es _
, popularizado en bibliotecas como subrayado y lodash. Ningún símbolo es más significativo que otro; Usted asigna el significado de su programa. Es simplemente JavaScript válido: D
$
cómo se usa. Si está preguntando acerca de la implementación en sí, $
es una función que recibe un valor x
y devuelve una nueva función k => ...
. Al observar el cuerpo de la función devuelta, vemos k (x)
que sabemos k
que también debe ser una función, y cualquiera que sea el resultado k (x)
se vuelve a poner $ (...)
, lo que sabemos devuelve otra k => ...
, y continúa ... Si todavía quedarse atascado, hágamelo saber.
abc(1,2,3)
es menos que ideal que abc(1)(2)(3)
. Es más difícil razonar sobre la lógica del código y es difícil leer la función abc y es más difícil leer la llamada a la función. Antes solo necesitabas saber qué hace abc, ahora no estás seguro de qué funciones sin nombre abc está volviendo a hacer, y dos veces más.
Comprender las sintaxis disponibles de las funciones de flecha le dará una idea de qué comportamiento están introduciendo cuando están "encadenados", como en los ejemplos que proporcionó.
Cuando una función de flecha se escribe sin llaves de bloque, con o sin múltiples parámetros, la expresión que constituye el cuerpo de la función se devuelve implícitamente . En su ejemplo, esa expresión es otra función de flecha.
No arrow funcs Implicitly return `e=>{…}` Explicitly return `e=>{…}`
---------------------------------------------------------------------------------
function (field) { | field => e => { | field => {
return function (e) { | | return e => {
e.preventDefault() | e.preventDefault() | e.preventDefault()
} | | }
} | } | }
Otra ventaja de escribir funciones anónimas usando la sintaxis de flecha es que están vinculadas léxicamente al alcance en el que están definidas. Desde 'Funciones de flecha' en MDN :
Una expresión de función de flecha tiene una sintaxis más corta en comparación con expresiones de función y enlaza léxicamente este valor. Las funciones de flecha son siempre anónimas .
Esto es particularmente pertinente en su ejemplo teniendo en cuenta que se toma de un reaccionarsolicitud. Como señaló @naomik, en React a menudo accedes a las funciones miembro de un componente usando this
. Por ejemplo:
Unbound Explicitly bound Implicitly bound
------------------------------------------------------------------------------
function (field) { | function (field) { | field => e => {
return function (e) { | return function (e) { |
this.setState(...) | this.setState(...) | this.setState(...)
} | }.bind(this) |
} | }.bind(this) | }
Un consejo general, si se confunde con alguna de las nuevas sintaxis de JS y cómo se compilará, puede verificar babel . Por ejemplo, copiar su código en babel y seleccionar el preajuste es2015 dará una salida como esta
handleChange = function handleChange(field) {
return function (e) {
e.preventDefault();
// Do something here
};
};
Piénselo así, cada vez que ve una flecha, la reemplaza por function
. function parameters
se definen antes de la flecha.
Entonces en tu ejemplo:
field => // function(field){}
e => { e.preventDefault(); } // function(e){e.preventDefault();}
y luego juntos:
function (field) {
return function (e) {
e.preventDefault();
};
}
// Basic syntax:
(param1, param2, paramN) => { statements }
(param1, param2, paramN) => expression
// equivalent to: => { return expression; }
// Parentheses are optional when there's only one argument:
singleParam => { statements }
singleParam => expression
this
.
Breve y simple 🎈
Es una función que devuelve otra función escrita de forma breve.
const handleChange = field => e => {
e.preventDefault()
// Do something here
}
// is equal to
function handleChange(field) {
return function(e) {
e.preventDefault()
// Do something here
}
}
¿Por qué la gente lo hace ?
¿Te has enfrentado cuando necesitas escribir una función que se puede personalizar? ¿O tiene que escribir una función de devolución de llamada que tenga parámetros fijos (argumentos), pero necesita pasar más variables a la función pero evitando las variables globales? Si su respuesta es " sí ", entonces es la forma de hacerlo.
Por ejemplo, tenemos una button
devolución de llamada con onClick. Y necesitamos pasar id
a la función, pero onClick
acepta solo un parámetro event
, no podemos pasar parámetros adicionales dentro de este:
const handleClick = (event, id) {
event.preventDefault()
// Dispatch some delete action by passing record id
}
¡No funcionará!
Por lo tanto, hacemos una función que devolverá otra función con su propio alcance de variables sin ninguna variable global, porque las variables globales son malvadas 😈.
¡Debajo de la función handleClick(props.id)}
se llamará y devolverá una función y tendrá id
su alcance! No importa cuántas veces se presione, los identificadores no se afectarán ni cambiarán entre sí, están totalmente aislados.
const handleClick = id => event {
event.preventDefault()
// Dispatch some delete action by passing record id
}
const Confirm = props => (
<div>
<h1>Are you sure to delete?</h1>
<button onClick={handleClick(props.id)}>
Delete
</button>
</div
)
El ejemplo en su pregunta es el de un curried function
que utiliza arrow function
y tiene un implicit return
para el primer argumento.
La función de flecha enlaza léxicamente esto, es decir, no tienen su propio this
argumento, pero toman el this
valor del ámbito de aplicación
Un equivalente del código anterior sería
const handleChange = (field) {
return function(e) {
e.preventDefault();
/// Do something here
}.bind(this);
}.bind(this);
Una cosa más a tener en cuenta sobre su ejemplo es que se define handleChange
como una constante o una función. Probablemente lo esté usando como parte de un método de clase y use unclass fields syntax
así que en lugar de vincular la función externa directamente, la vincularía en el constructor de la clase
class Something{
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(field) {
return function(e) {
e.preventDefault();
// do something
}
}
}
Otra cosa a tener en cuenta en el ejemplo es la diferencia entre el retorno implícito y explícito.
const abc = (field) => field * 2;
Arriba hay un ejemplo de retorno implícito, es decir. toma el campo de valor como argumento y devuelve el resultado field*2
que especifica explícitamente la función a devolver
Para un retorno explícito, le diría explícitamente al método que devuelva el valor
const abc = () => { return field*2; }
Otra cosa a tener en cuenta sobre las funciones de flecha es que no tienen las suyas propias, arguments
sino que también las heredan del ámbito de los padres.
Por ejemplo, si solo define una función de flecha como
const handleChange = () => {
console.log(arguments) // would give an error on running since arguments in undefined
}
Como alternativa, las funciones de flecha proporcionan los parámetros de descanso que puede usar
const handleChange = (...args) => {
console.log(args);
}
Puede que no esté totalmente relacionado, pero dado que la pregunta mencionada reacciona usa el caso (y sigo tropezando con este hilo SO): hay un aspecto importante de la función de doble flecha que no se menciona explícitamente aquí. Solo se nombra la 'primera' flecha (función) (y por lo tanto 'distinguible' por el tiempo de ejecución), las flechas siguientes son anónimas y desde el punto de vista de React cuentan como un objeto 'nuevo' en cada render.
Por lo tanto, la función de doble flecha hará que cualquier componente puro se vuelva a procesar todo el tiempo.
Ejemplo
Tiene un componente principal con un controlador de cambios como:
handleChange = task => event => { ... operations which uses both task and event... };
y con un render como:
{
tasks.map(task => <MyTask handleChange={this.handleChange(task)}/>
}
handleChange luego se usa en una entrada o haga clic. Y todo esto funciona y se ve muy bien. PERO significa que cualquier cambio que hará que los padres vuelvan a rendirse (como un cambio de estado completamente no relacionado) también volverá a procesar TODA su MyTask, aunque sean Componentes Puros.
Esto puede aliviarse de muchas maneras, como pasar la flecha 'más externa' y el objeto con el que lo alimentaría o escribir una función de actualización personalizada o volver a lo básico, como escribir funciones con nombre (y vincular esto manualmente ...)