Jak uzyskać prostą wysyłkę z this.props za pomocą connect w / Redux?

107

Mam prosty komponent React, który podłączam (mapowanie prostej tablicy / stanu). Aby uniknąć odwoływania się do kontekstu sklepu, chciałbym uzyskać sposób na uzyskanie „wysyłki” bezpośrednio z rekwizytów. Widziałem innych stosujących to podejście, ale z jakiegoś powodu nie mam do niego dostępu :)

Oto wersje każdej zależności npm, której obecnie używam

"react": "0.14.3",
"react-redux": "^4.0.0",
"react-router": "1.0.1",
"redux": "^3.0.4",
"redux-thunk": "^1.0.2"

Oto metoda komponentu z połączeniem

class Users extends React.Component {
    render() {
        const { people } = this.props;
        return (
            <div>
                <div>{this.props.children}</div>
                <button onClick={() => { this.props.dispatch({type: ActionTypes.ADD_USER, id: 4}); }}>Add User</button>
            </div>
        );
    }
};

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

export default connect(mapStateToProps, {
    fetchUsers
})(Users);

Jeśli chcesz zobaczyć reduktor (nic ekscytującego, ale oto jest)

const initialState = {
    people: []
};

export default function(state=initialState, action) {
    if (action.type === ActionTypes.ADD_USER) {
        let newPeople = state.people.concat([{id: action.id, name: 'wat'}]);
        return {people: newPeople};
    }
    return state;
};

Jeśli chcesz zobaczyć, jak skonfigurowany jest mój router w / Redux

const createStoreWithMiddleware = applyMiddleware(
      thunk
)(createStore);

const store = createStoreWithMiddleware(reducers);

var Route = (
  <Provider store={store}>
    <Router history={createBrowserHistory()}>
      {Routes}
    </Router>
  </Provider>
);

aktualizacja

wygląda na to, że jeśli pominę własną wysyłkę w połączeniu (obecnie powyżej pokazuję fetchUsers), otrzymam wysyłkę za darmo (tylko nie jestem pewien, czy tak zwykle działałaby konfiguracja z akcjami asynchronicznymi). Czy ludzie mieszają się i łączą, czy to wszystko, czy nic?

[mapDispatchToProps]

Toran Billups
źródło

Odpowiedzi:

280

Domyślnie mapDispatchToPropsjest to po prostu dispatch => ({ dispatch }).
Więc jeśli nie określisz drugiego argumentu connect(), zostaniesz dispatchwstawiony jako prop w swoim komponencie.

Jeśli przekażesz funkcję niestandardową do mapDispatchToProps, możesz zrobić wszystko z tą funkcją.
Kilka przykładów:

// inject onClick
function mapDispatchToProps(dispatch) {
  return {
    onClick: () => dispatch(increment())
  };
}

// inject onClick *and* dispatch
function mapDispatchToProps(dispatch) {
  return {
    dispatch,
    onClick: () => dispatch(increment())
  };
}

Aby zaoszczędzić trochę pisania Redux, bindActionCreators()który pozwala ci to zmienić:

// injects onPlusClick, onMinusClick
function mapDispatchToProps(dispatch) {
  return {
    onPlusClick: () => dispatch(increment()),
    onMinusClick: () => dispatch(decrement())
  };
}

zaangażowany w to:

import { bindActionCreators } from 'redux';

// injects onPlusClick, onMinusClick
function mapDispatchToProps(dispatch) {
  return bindActionCreators({
    onPlusClick: increment,
    onMinusClick: decrement
  }, dispatch);
}

lub nawet krócej, gdy nazwy rekwizytów pasują do nazw twórców działań:

// injects increment and decrement
function mapDispatchToProps(dispatch) {
  return bindActionCreators({ increment, decrement }, dispatch);
}

Jeśli chcesz, na pewno możesz dodać dispatchręcznie:

// injects increment, decrement, and dispatch itself
function mapDispatchToProps(dispatch) {
  return {
    ...bindActionCreators({ increment, decrement }), // es7 spread syntax
    dispatch
  };
}

Nie ma oficjalnej porady, czy powinieneś to zrobić, czy nie. connect()zwykle służy jako granica między komponentami świadomymi i nieświadomymi Redux. Dlatego zwykle uważamy, że nie ma sensu wprowadzać zarówno powiązanych twórców akcji, jak i dispatch. Ale jeśli czujesz, że musisz to zrobić, nie krępuj się.

Wreszcie wzorzec, którego teraz używasz, to skrót nawet krótszy niż dzwonienie bindActionCreators. Kiedy wszystko, co robisz, to zwracanie bindActionCreators, możesz pominąć wywołanie, więc zamiast robić to:

// injects increment and decrement
function mapDispatchToProps(dispatch) {
  return bindActionCreators({ increment, decrement }, dispatch);
}

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

można zapisać w ten sposób

export default connect(
  mapStateToProps,
  { increment, decrement } // injects increment and decrement
)(App);

Będziesz jednak musiał zrezygnować z tej ładnej, krótkiej składni, gdy chcesz czegoś bardziej niestandardowego, na przykład przejścia dispatch.

Dan Abramov
źródło
2
@DanAbramov btw jest drugim parametrem bindActionCreators(actionCreators, dispatch)opcjonalnym? Zauważyłem w twoim kodzie na // es7 spread syntaxlinii, że dispatchnie jest przekazywany dobindActionCreators
yonasstephen
co to jest metoda inkrementacji wewnętrznej?
Khurshid Ansari
Składnia spreadu es7 powinna wyglądać następująco function mapDispatchToProps(dispatch) { return { ...bindActionCreators({ increment, decrement }), dispatch }; }
Black
6

Zwykle możesz mieszać i dopasowywać w zależności od tego, co chcesz.

Państwo może przekazać dispatchna jako rekwizyt jeśli to, co chcesz:

export default connect(mapStateToProps, (dispatch) => ({
    ...bindActionCreators({fetchUsers}, dispatch), dispatch
}))(Users);

Nie jestem pewien, jak fetchUsersjest używany (jako funkcji asynchroniczny?), Ale można zazwyczaj używać coś jak bindActionCreatorsna auto-wiążą wysyłki i wtedy nie trzeba się martwić o użyciu dispatchbezpośrednio podłączonych komponentów.

Używanie dispatchrodzaju katalogowego łączy głupi , bezstanowy składnik z reduxem. Co może sprawić, że będzie mniej przenośny.

Davin Tryon
źródło
3
To, co sugerujesz, nie zadziała. Otrzymasz niezwiązany fetchUserswstrzyknięty jako rekwizyt. Notacja skrótu działa tylko wtedy, gdy przekażesz obiekt jako drugi argument. Po przejechaniu funkcję trzeba nazwać bindActionCreatorssiebie: dispatch => ({ ...bindActionCreators({ fetchUsers }, dispatch), dispatch }).
Dan Abramov
Po co rozpowszechniać i przekazywać wysyłkę bezpośrednio w bindActionCreators? dispatch => ( bindActionCreators({ dispatch, fetchUsers }, dispatch))
user3711421
2

Chociaż możesz przekazać dispatchjako część dispatchToProps, zalecałbym unikanie dostępu do storelub dispatchbezpośrednio z poziomu komponentów. Wygląda na to, że lepiej byłoby, gdybyś przekazał związany kreator akcji w drugim argumencie connectadispatchToProps

Zobacz przykład, który zamieściłem tutaj https://stackoverflow.com/a/34455431/2644281, jak przekazać „już związanego kreatora akcji” w ten sposób, aby komponenty nie musiały bezpośrednio wiedzieć o sklepie / wysyłce ani od niego zależeć .

Przepraszam, że mówię krótko. Zaktualizuję więcej informacji.

Erik Aybar
źródło