BindActionCreators react / redux'te ne zaman kullanılır?


93

BindActionCreators için Redux belgeleri şunları belirtir:

Bunun için tek kullanım durumu bindActionCreators, bazı eylem yaratıcılarını Redux'un farkında olmayan bir bileşene aktarmak istediğinizde ve sevkıyat veya Redux mağazasını ona iletmek istemediğinizde.

Nerede bindActionCreatorskullanılacak / ihtiyaç duyulacak bir örnek ne olabilir?

Hangi tür bileşen Redux'tan haberdar olmaz ?

Her iki seçeneğin avantajları / dezavantajları nelerdir?

//actionCreator
import * as actionCreators from './actionCreators'

function mapStateToProps(state) {
  return {
    posts: state.posts,
    comments: state.comments
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(actionCreators, dispatch)
}

vs

function mapStateToProps(state) {
  return {
    posts: state.posts,
    comments: state.comments
  }
}

function mapDispatchToProps(dispatch) {
  return {
    someCallback: (postId, index) => {
      dispatch({
        type: 'REMOVE_COMMENT',
        postId,
        index
      })
    }
  }
}

Yanıtlar:


59

En popüler cevabın aslında soruyu ele aldığını sanmıyorum.

Aşağıdaki örneklerin tümü esasen aynı şeyi yapar ve "ön bağlama" yok konseptini takip eder.

// option 1
const mapDispatchToProps = (dispatch) => ({
  action: () => dispatch(action())
})


// option 2
const mapDispatchToProps = (dispatch) => ({
  action: bindActionCreators(action, dispatch)
})


// option 3
const mapDispatchToProps = {
  action: action
}

Seçenek #3, seçenek için sadece bir kısaltmadır #1, bu nedenle asıl soru neden seçeneğe #1karşı seçenek kullanılmalıdır #2? Her ikisinin de react-redux kod tabanında kullanıldığını gördüm ve oldukça kafa karıştırıcı buluyorum.

Ben karışıklık tüm olmasından kaynaklanır düşünüyorum örneklerde de react-reduxdoc kullanımlar bindActionCreatorsiçin doc ederken bindActionCreators (Söz kendisi naklen) tepki-redux ile kullanmayın diyor.

Sanırım cevabın kod tabanındaki tutarlılık, ancak kişisel olarak gerektiğinde eylemleri gönderime açıkça sarmayı tercih ediyorum .


3
seçenek nasıl bir seçenek için #3bir kısaltmadır #1?
ogostos


@ArtemBernatskyi teşekkürler. Orada 3 vaka içindir dışarı Yani, döner mapDispatchToProps: function, objectve eksik. Her vakanın nasıl ele
alınacağı

Bu cevabı arıyordum. Teşekkürler.
Kishan Rajdev

Aslında, React belgelerinin şimdi "bazı eylem yaratıcıları Redux'un farkında olmayan bir bileşene aktarın" özel bir kullanım durumuyla karşılaştım çünkü daha karmaşık bir bileşene bağlı basit bir bileşene sahibim ve ek yükü ekledim arasında reduxve connectve addDispatchToPropsben sadece bir prop aşağı geçebilir eğer basit bileşen overkill görünüyor için. Ama ben farkında neredeyse bütün davaları olduğum kadarıyla mapDispatchya seçenekleri olacaktır sahne için #1ya #3cevap belirtilen
icc97

48

Zamanın% 99'u connect(), mapDispatchToPropsparametrenin bir parçası olarak React-Redux işlevi ile kullanılır . mapDispatchSağladığınız işlevin içinde açıkça kullanılabilir veya nesne kısa sözdizimini kullanırsanız ve eylem yaratıcılarıyla dolu bir nesneyi iletirseniz otomatik olarak kullanılabilir connect.

Buradaki fikir, eylem yaratıcılarını önceden bağlayarak, connect()teknik olarak ilettiğiniz bileşenin bağlı olduğunu "bilmiyor" - sadece çalışması gerektiğini biliyor this.props.someCallback(). Öte yandan, eylem yaratıcılarını bağlamadıysanız ve çağırdıysanız, this.props.dispatch(someActionCreator())bileşen artık var props.dispatcholmayı beklediği için bağlı olduğunu "bilir" .

Idiomatic Redux: Neden eylem oluşturucuları kullanmalıyım? Blog gönderimde bu konu hakkında bazı düşünceler yazdım. .


1
Ama bu iyi olan 'mapDispatchToProps' ile bağlantılı. Bağlayıcı eylem yaratıcıları aslında negatif / anlamsız bir şey gibi görünüyor çünkü hata ayıklama gibi diğer birçok şeyin yanı sıra işlev tanımını (TS veya Akış) kaybedeceksiniz. Yeni projelerimde hiç kullanmıyorum ve bugüne kadar bir sorun yaşamadım. Ayrıca Saga ve kalıcı durumu kullanıyor. Ayrıca, sadece redux işlevlerini çağırıyorsanız (yine de hoş bir tıklama alıyorsunuz), bence dağınık ve anlamsız olan eylem yaratıcılarını 'eklemekten' daha temiz olduğunu söyleyebilirim. Akıllı cihazınız (genellikle ekran bileşenleri), yalnızca sahne için yine de bağlanabilir.
Oliver Dixon

19

Daha eksiksiz bir örnek, bağlanmak için eylem yaratıcılarıyla dolu bir nesneyi iletin:

import * as ProductActions from './ProductActions';

// component part
export function Product({ name, description }) {
    return <div>
        <button onClick={this.props.addProduct}>Add a product</button>
    </div>
}

// container part
function mapStateToProps(state) {
    return {...state};
}

function mapDispatchToProps(dispatch) {
    return bindActionCreators({
        ...ProductActions,
    }, dispatch);
}

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

cevap bu olmalı
Deen John

16

Orijinal soruları cevaplamaya çalışacağım ...

Smart & Dumb bileşenleri

İlk sorunuzda temel olarak neden bindActionCreatorsilk etapta gerekli olduğunu ve Redux'tan ne tür bileşenlerin farkında olmaması gerektiğini soruyorsunuz .

Kısacası, buradaki fikir, bileşenlerin akıllı (kapsayıcı) ve aptal (sunum) bileşenlere bölünmesi gerektiğidir . Aptal bileşenler , bilinmesi gerekenler temelinde çalışır. Onların asıl işi, verilen veriyi HTML'ye dönüştürmektir, başka bir şey değildir. Uygulamanın iç işleyişinden haberdar olmamalıdırlar. Uygulamanızın cilt derin ön tabakası olarak görülebilirler.

Öte yandan akıllı bileşenler , aptal bileşenler için veri hazırlayan ve tercihen HTML oluşturma yapmayan bir tür yapıştırıcıdır .

Bu tür bir mimari, UI katmanı ile altındaki veri katmanı arasındaki gevşek bağlantıyı destekler. Bu da, iki katmandan herhangi birinin, diğer katmanı bozmayacak başka bir şeyle (yani kullanıcı arayüzünün yeni bir tasarımı) kolayca değiştirilmesini sağlar.

Sorunuzu cevaplamak için: aptal bileşenler Redux'tan (veya bu konuda veri katmanının gereksiz uygulama detaylarından) haberdar olmamalıdır çünkü gelecekte onu başka bir şeyle değiştirmek isteyebiliriz.

Bu kavram hakkında daha fazla bilgiyi Redux kılavuzunda ve daha ayrıntılı olarak Dan Abramov'un Sunum ve Konteyner Bileşenleri makalesinde bulabilirsiniz .

Hangi örnek daha iyi

İkinci soru, verilen örneklerin avantajları / dezavantajları ile ilgiliydi.

Olarak ilk örnek aksiyon yaratıcıları, ayrı tanımlanır actionCreatorsbaşka bir yerde tekrar kullanılabilir, yani dosya / modülü. Eylemleri tanımlamanın hemen hemen standart yolu. Bunda gerçekten herhangi bir dezavantaj görmüyorum.

İkinci örnek, birden çok dezavantajları vardır ki, sıralı işlem yaratıcıları tanımlar:

  • eylem yaratıcıları yeniden kullanılamaz (tabii ki)
  • şey daha ayrıntılı, bu da daha az okunabilir hale geliyor
  • eylem türleri sabit kodlanmıştır - tercihen bunları constsayrı ayrı tanımlamak , böylece indirgeyicilerde başvurulabilirler - bu, yazım hataları olasılığını azaltır
  • Eylem oluşturucuları satır içi olarak tanımlamak, bunları kullanmanın önerilen / beklenen yoluna aykırıdır - bu, kodunuzu paylaşmayı planlamanız durumunda, kodunuzu topluluk için biraz daha az okunabilir hale getirir.

İkinci örneğin birincisine göre bir avantajı var - yazmak daha hızlı! Dolayısıyla, kodunuz için daha büyük planlarınız yoksa, sorun olmayabilir.

Umarım işleri biraz açıklığa kavuşturmayı başardım ...


2

Olası bir kullanım, bindActionCreators()birden çok eylemi tek bir destek olarak birlikte "eşlemek" tir.

Normal bir gönderi şuna benzer:

Sahne alanlarına birkaç genel kullanıcı eylemini eşleyin.

const mapStateToProps = (state: IAppState) => {
  return {
    // map state here
  }
}
const mapDispatchToProps = (dispatch: Dispatch) => {
  return {
    userLogin: () => {
      dispatch(login());
    },
    userEditEmail: () => {
      dispatch(editEmail());
    },
  };
};
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);

Daha büyük projelerde, her gönderinin ayrı ayrı haritalanması işin zor gelebilir. Birbiriyle ilişkili bir dizi eylemimiz varsa, bu eylemleri birleştirebiliriz . Örneğin, kullanıcıyla ilgili her türlü farklı eylemi gerçekleştiren bir kullanıcı eylem dosyası. Her eylemi ayrı bir gönderi olarak çağırmak bindActionCreators()yerine kullanabiliriz dispatch.

BindActionCreators () kullanarak Birden Çok Gönderim

İlgili tüm eylemlerinizi içe aktarın. Redux mağazasında muhtemelen hepsi aynı dosyadadır.

import * as allUserActions from "./store/actions/user";

Ve şimdi dağıtımı kullanmak yerine bindActionCreators () kullanın

    const mapDispatchToProps = (dispatch: Dispatch) => {
      return {
           ...bindActionCreators(allUserActions, dispatch);
        },
      };
    };
    export default connect(mapStateToProps, mapDispatchToProps, 
    (stateProps, dispatchProps, ownProps) => {
      return {
        ...stateProps,
        userAction: dispatchProps
        ownProps,
      }
    })(MyComponent);

Şimdi userAction, bileşeninizdeki tüm eylemleri çağırmak için pervaneyi kullanabilirim .

IE: userAction.login() userAction.editEmail() veya this.props.userAction.login() this.props.userAction.editEmail().

NOT: bindActionCreators () 'ı tek bir pervaneye eşlemeniz gerekmez. (Ek => {return {}}o eşleştiren userAction). bindActionCreators()Tek bir dosyanın tüm eylemlerini ayrı sahne olarak eşlemek için de kullanabilirsiniz . Ama bunu yapmak kafa karıştırıcı olabilir. Her bir eylem veya "eylem grubuna" açık bir ad verilmesini tercih ederim. Ayrıca ownPropsbu "çocuk aksesuarlarının" ne olduğu veya nereden geldikleri hakkında daha açıklayıcı olmaları için isim vermek isterim . Redux + React'i kullanırken, tüm aksesuarların tedarik edildiği yerde biraz kafa karıştırıcı olabilir, bu nedenle ne kadar açıklayıcı olursa o kadar iyidir.


2

kullanarak bindActionCreators, birden fazla eylem işlevini gruplayabilir ve bu şekilde Redux (Aptal Bileşen) farkında olmayan bir bileşene aktarabilir.

// actions.js

export const increment = () => ({
    type: 'INCREMENT'
})

export const decrement = () => ({
    type: 'DECREMENT'
})
// main.js
import { Component } from 'react'
import { bindActionCreators } from 'redux'
import * as Actions from './actions.js'
import Counter from './counter.js'

class Main extends Component {

  constructor(props) {
    super(props);
    const { dispatch } = props;
    this.boundActionCreators = bindActionCreators(Actions, dispatch)
  }

  render() {
    return (
      <Counter {...this.boundActionCreators} />
    )
  }
}
// counter.js
import { Component } from 'react'

export default Counter extends Component {
  render() {
    <div>
     <button onclick={() => this.props.increment()}
     <button onclick={() => this.props.decrement()}
    </div>
  }
}

1

Ayrıca bindActionsCreators hakkında daha fazla bilgi arıyordum ve işte projemde nasıl uyguladığım.

// Actions.js
// Action Creator
const loginRequest = (username, password) => {
 return {
   type: 'LOGIN_REQUEST',
   username,
   password,
  }
}

const logoutRequest = () => {
 return {
   type: 'LOGOUT_REQUEST'
  }
}

export default { loginRequest, logoutRequest };

React Bileşeninizde

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ActionCreators from './actions'

class App extends Component {
  componentDidMount() {
   // now you can access your action creators from props.
    this.props.loginRequest('username', 'password');
  }

  render() {
    return null;
  }
}

const mapStateToProps = () => null;

const mapDispatchToProps = dispatch => ({ ...bindActionCreators(ActionCreators, dispatch) });

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

0

One güzel bir kullanım durumu bindActionCreatorsile entegrasyon içindir redux-destan kullanarak redux-destan-rutinleri . Örneğin:

// routines.js
import { createRoutine } from "redux-saga-routines";
export const fetchPosts = createRoutine("FETCH_POSTS");
// Posts.js
import React from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { fetchPosts } from "routines";

class Posts extends React.Component {
  componentDidMount() {
    const { fetchPosts } = this.props;
    fetchPosts();
  }

  render() {
    const { posts } = this.props;
    return (
      <ul>
        {posts.map((post, i) => (
          <li key={i}>{post}</li>
        ))}
      </ul>
    );
  }
}

const mapStateToProps = ({ posts }) => ({ posts });
const mapDispatchToProps = dispatch => ({
  ...bindActionCreators({ fetchPosts }, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Posts);
// reducers.js
import { fetchPosts } from "routines";

const initialState = [];

export const posts = (state = initialState, { type, payload }) => {
  switch (type) {
    case fetchPosts.SUCCESS:
      return payload.data;
    default:
      return state;
  }
};
// api.js
import axios from "axios";

export const JSON_OPTS = { headers: { Accept: "application/json" } };
export const GET = (url, opts) =>
  axios.get(url, opts).then(({ data, headers }) => ({ data, headers }));
// sagas.js
import { GET, JSON_OPTS } from "api";
import { fetchPosts } from "routines";
import { call, put, takeLatest } from "redux-saga/effects";

export function* fetchPostsSaga() {
  try {
    yield put(fetchPosts.request());
    const { data } = yield call(GET, "/api/posts", JSON_OPTS);
    yield put(fetchPosts.success(data));
  } catch (error) {
    if (error.response) {
      const { status, data } = error.response;
      yield put(fetchPosts.failure({ status, data }));
    } else {
      yield put(fetchPosts.failure(error.message));
    }
  } finally {
    yield put(fetchPosts.fulfill());
  }
}

export function* fetchPostsRequestSaga() {
  yield takeLatest(fetchPosts.TRIGGER, fetchPostsSaga);
}

Bu modelin React Hook'lar kullanılarak uygulanabileceğini unutmayın (React 16.8'den itibaren).


-1

Docs ifadesi çok açıktır:

Bunun için tek kullanım durumu bindActionCreators, bazı eylem yaratıcılarını Redux'un farkında olmayan bir bileşene aktarmak istediğinizde ve sevkıyat veya Redux mağazasını ona iletmek istemediğinizde.

Bu açıkça, aşağıdaki ve yalnızca bir koşulda ortaya çıkabilecek bir kullanım durumudur:

Diyelim ki, A ve B bileşenimiz var:

// A use connect and updates the redux store
const A = props => {}
export default connect()(A)

// B doesn't use connect therefore it does not know about the redux store.
const B = props => {}
export default B

React-redux için enjekte edin: (A)

const boundActionCreators = bindActionCreators(SomeActionCreator, dispatch)
// myActionCreatorMethod,
// myActionCreatorMethod2,
// myActionCreatorMethod3,

// when we want to dispatch
const action = SomeActionCreator.myActionCreatorMethod('My updates')
dispatch(action)

React-redux tarafından enjekte edildi: (B)

const { myActionCreatorMethod } = props
<B myActionCreatorMethod={myActionCreatorMethod} {...boundActionCreators} />

Aşağıdakileri fark ettiniz mi?

  • Bileşen B'deki redux mağazasını bilmediğimiz halde, redux deposunu A bileşeni aracılığıyla güncelledik.

  • A bileşenini güncellemiyoruz. Tam olarak ne demek istediğimi anlamak için bu yazıyı inceleyebilirsiniz . Umarım bir fikrin olur.


7
Hiçbir şey anlamadım
vsync
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.