Co to jest „@” (symbol) w dekoratorze Redux @connect?

226

Uczę się Redux z React i natknąłem się na ten kod. Nie jestem pewien, czy jest on specyficzny dla Redux, czy nie, ale widziałem następujący fragment kodu w jednym z przykładów.

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

Chociaż funkcjonalność connectjest dość prosta, ale nie rozumiem @wcześniej connect. To nie jest nawet operator JavaScript, jeśli się nie mylę.

Czy ktoś może wyjaśnić, co to jest i dlaczego jest używane?

Aktualizacja:

Jest to w rzeczywistości część, react-reduxktóra służy do połączenia komponentu React ze sklepem Redux.

Salman
źródło
6
Nie znam Redux, ale wygląda jak dekorator. medium.com/google-developers/…
Lee
4
Uwielbiam to, jak w tym nowym świecie JavaScript wpatrujesz się w kod przez połowę czasu i zastanawiasz się „jaka jest część składni języka?”
MK.
4
Lol, jestem teraz głęboko w reduksie i tym podobne. Ale wtedy nie wiedziałem, że składnia dekoratora nie ma nic wspólnego z redux. To tylko JavaScript. Cieszę się, że to pytanie pomaga wielu osobom takim jak ja. :)
Salman
1
Najwyraźniej zespół redux odradza używanie Connect jako dekoratora w tej chwili github.com/happypoulp/redux-tutorial/issues/87
Syed

Odpowiedzi:

376

Ten @symbol jest w rzeczywistości wyrażeniem JavaScript proponowanym obecnie dla oznaczenia dekoratorów :

Dekoratorzy umożliwiają dodawanie adnotacji i modyfikowanie klas i właściwości w czasie projektowania.

Oto przykład konfiguracji Redux bez i z dekoratorem:

Bez dekoratora

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);

Korzystanie z dekoratora

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
}

Oba powyższe przykłady są równoważne, to tylko kwestia preferencji. Ponadto składnia dekoratora nie jest jeszcze wbudowana w żadne środowiska wykonawcze JavaScript i jest nadal eksperymentalna i może ulec zmianie. Jeśli chcesz go użyć, jest on dostępny przy użyciu Babel .

Tanner Semerad
źródło
46
to jest niesamowite
svnm
2
Można być jeszcze bardziej zwięzłym dzięki składni ES6. @connect (state => {return {todos: state.todos};}, dispatch => {return {actions: bindActionCreators (actionCreators, dispatch)};})
LessQuesar 24.10.2016
11
Jeśli naprawdę chcesz być zwięzły, możesz użyć ukrytych zwrotów w ES6. To zależy od tego, jak wyraźnie chcesz być. @connect(state => ({todos: state.todos}), dispatch => ({actions: bindActionCreators(actionCreators, dispatch)}))
Tanner Semerad
3
Jak wyeksportowałbyś niepowiązany komponent do testów jednostkowych?
tim
Korzystanie z dekoratora do reduxu z nawigacją reagującą może być problematyczne, obecnie najlepszą praktyką jest używanie funkcji, a nie dekoratora: github.com/react-community/react-navigation/issues/1180
straya
50

Bardzo ważne!

Te rekwizyty są nazywane rekwizytami stanowymi i różnią się od zwykłych rekwizytów, każda zmiana rekwizytów stanu komponentu będzie wywoływać metodę renderowania komponentu raz po raz, nawet jeśli nie użyjesz tych rekwizytów, dlatego ze względów wydajnościowych spróbuj powiązać tylko komponent rekwizyty stanu, których potrzebujesz w swoim komponencie, a jeśli używasz rekwizytów podrzędnych, wiąż te rekwizyty.

przykład: powiedzmy, że w twoim komponencie potrzebujesz tylko dwóch rekwizytów:

  1. ostatnia wiadomość
  2. Nazwa użytkownika

nie rób tego

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

Zrób to

@connect(state => ({ 
   user_name: state.user.name,
   last_message: state.messages[state.messages.length-1]
}))
Fareed Alnamrouti
źródło
9
lub użyj selektorów, takich jak reselect lub fastmemoize
Julius Koronci