O que é o '@' (no símbolo) no decorador Redux @connect?

226

Estou aprendendo Redux com React e me deparei com esse código. Não tenho certeza se é específico ao Redux ou não, mas vi o seguinte trecho de código em um dos exemplos.

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

Embora a funcionalidade de connectseja bastante direta, mas eu não entendo o @antes connect. Não é nem mesmo um operador JavaScript se não estiver errado.

Alguém pode explicar por favor o que é isso e por que é usado?

Atualizar:

De fato, parte react-reduxdisso é usada para conectar um componente React a um repositório Redux.

Salman
fonte
6
Não conheço o Redux, mas parece um decorador. medium.com/google-developers/…
Lee
4
Eu amo como neste novo mundo JavaScript você está encarando o código metade do tempo e pensando "que parte da sintaxe da linguagem é essa?"
MK.
4
Muitas gargalhadas, estou profundamente metido em redux e outras coisas agora. Mas naquela época eu não sabia que a sintaxe do decorador não tinha nada a ver com redux. É apenas JavaScript. Fico feliz em ver esta pergunta está ajudando muitas pessoas como eu. :)
Salman
1
Aparentemente, a equipa redux desencoraja o uso de conexão como um decorador, no momento github.com/happypoulp/redux-tutorial/issues/87
Syed Jafri

Respostas:

376

O @símbolo é de fato uma expressão JavaScript atualmente proposta para significar decoradores :

Os decoradores possibilitam anotar e modificar classes e propriedades em tempo de design.

Aqui está um exemplo de configuração do Redux sem e com um decorador:

Sem 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 um 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
}

Os dois exemplos acima são equivalentes, é apenas uma questão de preferência. Além disso, a sintaxe do decorador ainda não está incorporada em nenhum tempo de execução do Javascript e ainda é experimental e sujeita a alterações. Se você quiser usá-lo, ele estará disponível usando o Babel .

Tanner Semerad
fonte
46
isto é incrível
svnm
2
Pode-se ainda ser mais conciso com a sintaxe do ES6. @connect (state => {return {todos: state.todos};}, dispatch => {return {actions: bindActionCreators (actionCreators, dispatch)};})
LessQuesar
11
Se você realmente quer ser conciso, pode usar retornos implícitos no ES6. Depende de quão explícito você deseja ser. @connect(state => ({todos: state.todos}), dispatch => ({actions: bindActionCreators(actionCreators, dispatch)}))
Tanner Semerad 24/10
3
Como você exportaria o componente não conectado para teste de unidade?
tim
Usar o decorador para redux com navegação de reação pode ser problemático, a melhor prática atual é usar a função e não o decorador: github.com/react-community/react-navigation/issues/1180
straya
50

Muito importante!

Esses adereços são chamados adereços de estado e são diferentes dos adereços normais; qualquer alteração nos adereços de estado do componente acionará o método de renderização do componente repetidas vezes, mesmo se você não os usar, portanto, por razões de desempenho, tente ligar apenas ao componente os adereços de estado que você precisa dentro do seu componente e se você usar um sub adereços, vincule-os apenas.

exemplo: digamos que dentro do seu componente você precise apenas de dois adereços:

  1. a última mensagem
  2. o nome de usuário

não faça isso

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

faça isso

@connect(state => ({ 
   user_name: state.user.name,
   last_message: state.messages[state.messages.length-1]
}))
Alnamrouti com tarifa
fonte
9
ou usar seletores como reselect ou fastmemoize
Julius Koronci