¿Cuál es la '@' (en el símbolo) en el decorador Redux @connect?

226

Estoy aprendiendo Redux con React y me topé con este código. No estoy seguro de si es específico de Redux o no, pero he visto el siguiente fragmento de código en uno de los ejemplos.

@connect((state) => {
  return {
    key: state.a.b
  };
})

Si bien la funcionalidad de connectes bastante sencilla, pero no entiendo el @antes connect. Ni siquiera es un operador de JavaScript si no me equivoco.

¿Alguien puede explicar por favor qué es esto y por qué se usa?

Actualizar:

De hecho, es una parte de la react-reduxcual se utiliza para conectar un componente React a una tienda Redux.

Salman
fuente
66
No estoy familiarizado con Redux, pero parece un decorador. medium.com/google-developers/…
Lee
44
Me encanta cómo en este nuevo mundo JavaScript estás mirando el código la mitad del tiempo y pensando "¿qué parte de la sintaxis del lenguaje es esta?"
MK.
44
Lol, estoy muy metido en el redux y esas cosas ahora. Pero en aquel entonces no sabía que la sintaxis del decorador no tenía nada que ver con redux. Es solo JavaScript. Me alegra ver que esta pregunta está ayudando a mucha gente como yo. :)
Salman
1
Aparentemente, el equipo de redux desaconseja el uso de connect como decorador en este momento github.com/happypoulp/redux-tutorial/issues/87
Syed Jafri

Respuestas:

376

El @símbolo es, de hecho, una expresión de JavaScript actualmente propuesta para significar decoradores :

Los decoradores permiten anotar y modificar clases y propiedades en tiempo de diseño.

Aquí hay un ejemplo de cómo configurar Redux sin y con un decorador:

Sin un decorador

import React from 'react';
import * as actionCreators from './actionCreators';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

function mapStateToProps(state) {
  return { todos: state.todos };
}

function mapDispatchToProps(dispatch) {
  return { actions: bindActionCreators(actionCreators, dispatch) };
}

class MyApp extends React.Component {
  // ...define your main app here
}

export default connect(mapStateToProps, mapDispatchToProps)(MyApp);

Usando un decorador

import React from 'react';
import * as actionCreators from './actionCreators';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

function mapStateToProps(state) {
  return { todos: state.todos };
}

function mapDispatchToProps(dispatch) {
  return { actions: bindActionCreators(actionCreators, dispatch) };
}

@connect(mapStateToProps, mapDispatchToProps)
export default class MyApp extends React.Component {
  // ...define your main app here
}

Ambos ejemplos anteriores son equivalentes, es solo una cuestión de preferencia. Además, la sintaxis del decorador aún no está integrada en ningún tiempo de ejecución de Javascript, y aún es experimental y está sujeta a cambios. Si desea usarlo, está disponible con Babel .

Tanner Semerad
fuente
46
esto es increíble
svnm
2
Incluso se puede ser más conciso con la sintaxis de ES6. @connect (state => {return {todos: state.todos};}, dispatch => {return {actions: bindActionCreators (actionCreators, dispatch)};})
LessQuesar
11
Si realmente quiere ser conciso, puede usar retornos implícitos en ES6. Depende de cuán explícito quieras ser. @connect(state => ({todos: state.todos}), dispatch => ({actions: bindActionCreators(actionCreators, dispatch)}))
Tanner Semerad
3
¿Cómo exportaría el componente desconectado para pruebas unitarias?
tim
Usar decorador para redux con react-navigation puede ser problemático, la mejor práctica actual es usar la función no el decorador: github.com/react-community/react-navigation/issues/1180
straya
50

¡Muy importante!

Estos accesorios se denominan accesorios de estado y son diferentes de los accesorios normales, cualquier cambio en los accesorios de estado de su componente activará el método de representación del componente una y otra vez, incluso si no usa estos accesorios, por lo que por razones de rendimiento intente vincularse solo a su componente los accesorios de estado que necesita dentro de su componente y si usa un accesorio secundario, solo enlace estos accesorios.

ejemplo: digamos que dentro de tu componente solo necesitas dos accesorios:

  1. el ultimo mensaje
  2. el nombre de usuario

no hagas esto

@connect(state => ({ 
   user: state.user,
   messages: state.messages
}))

hacer esto

@connect(state => ({ 
   user_name: state.user.name,
   last_message: state.messages[state.messages.length-1]
}))
Alnamrouti Fareed
fuente
99
o use selectores como reselect o fastmemoize
Julius Koronci