Kapan bindActionCreators digunakan di react / redux?


93

Dokumen Redux untuk bindActionCreators menyatakan bahwa:

Satu-satunya kasus penggunaan bindActionCreatorsadalah ketika Anda ingin meneruskan beberapa pembuat tindakan ke komponen yang tidak mengetahui Redux, dan Anda tidak ingin meneruskan pengiriman atau penyimpanan Redux ke sana.

Apa contoh yang bindActionCreatorsakan digunakan / dibutuhkan?

Jenis komponen apa yang tidak mengetahui Redux ?

Apa keuntungan / kerugian dari kedua opsi tersebut?

//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
      })
    }
  }
}

Jawaban:


59

Saya tidak berpikir bahwa jawaban yang paling populer sebenarnya menjawab pertanyaan itu.

Semua contoh di bawah pada dasarnya melakukan hal yang sama dan mengikuti konsep tidak ada "pra-pengikatan".

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


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


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

Opsi #3hanyalah singkatan dari opsi #1, jadi pertanyaan sebenarnya mengapa seseorang akan menggunakan opsi #1vs opsi #2. Saya telah melihat keduanya digunakan dalam basis kode react-redux, dan saya merasa itu agak membingungkan.

Saya pikir kebingungan berasal dari fakta bahwa semua contoh di react-reduxdoc digunakan bindActionCreatorssementara doc untuk bindActionCreators (seperti dikutip dalam pertanyaan itu sendiri) mengatakan untuk tidak menggunakannya dengan react-redux.

Saya kira jawabannya adalah konsistensi dalam basis kode, tetapi saya pribadi lebih suka secara eksplisit membungkus tindakan dalam pengiriman kapan pun diperlukan.


3
bagaimana opsi #3merupakan singkatan dari opsi #1?
ogostos


@ArtemBibit terima kasih. Jadi, ternyata ada 3 kasus untuk mapDispatchToProps: function, objectdan hilang. Cara menangani setiap kasus dijelaskan di sini
ogostos

Saya telah mencari jawaban ini. Terima kasih.
Kishan Rajdev

Saya benar-benar menemukan kasus penggunaan spesifik yang dibicarakan oleh dokumen React sekarang "berikan beberapa pembuat tindakan ke komponen yang tidak mengetahui Redux" karena saya memiliki komponen sederhana yang terhubung ke komponen yang lebih kompleks dan menambahkan overhead dari reduxdan connectdan addDispatchToPropsuntuk komponen sederhana tampaknya berlebihan jika saya bisa melewatkan satu prop ke bawah. Tapi sejauh yang saya tahu hampir semua kasus untuk mapDispatchprops akan menjadi opsi #1atau #3disebutkan dalam jawaban
icc97

48

99% dari waktu, ini digunakan dengan fungsi React-Redux connect(), sebagai bagian dari mapDispatchToPropsparameter. Ini dapat digunakan secara eksplisit di dalam mapDispatchfungsi yang Anda sediakan, atau secara otomatis jika Anda menggunakan sintaks singkat objek dan meneruskan objek yang penuh dengan pembuat tindakan connect.

Idenya adalah bahwa dengan mengikat sebelumnya pembuat tindakan, komponen yang Anda berikan secara connect()teknis "tidak tahu" bahwa itu terhubung - hanya tahu bahwa itu perlu dijalankan this.props.someCallback(). Di sisi lain, jika Anda tidak mengikat pembuat tindakan, dan menelepon this.props.dispatch(someActionCreator()), sekarang komponen "tahu" bahwa itu terhubung karena mengharapkan props.dispatchada.

Saya menulis beberapa pemikiran tentang topik ini di posting blog saya Idiomatic Redux: Mengapa menggunakan pembuat tindakan? .


1
Tapi itu terhubung dengan 'mapDispatchToProps', yang tidak masalah. Tampaknya pembuat tindakan mengikat sebenarnya adalah hal negatif / tidak berguna karena Anda akan kehilangan definisi fungsi (TS atau Arus) di antara banyak hal lain seperti debugging. Pada proyek baru saya, saya tidak pernah menggunakannya dan saya tidak memiliki masalah hingga saat ini. Juga menggunakan Saga dan status persist. Juga, jika Anda hanya memanggil fungsi redux (Anda mendapatkan klik yang bagus) saya akan mengatakan itu lebih bersih daripada 'melampirkan' pembuat tindakan yang menurut saya berantakan dan tidak berguna. Smart Anda (biasanya komponen layar) masih bisa menggunakan connect, hanya untuk props saja.
Oliver Dixon

19

Contoh yang lebih lengkap, berikan objek yang penuh dengan pembuat aksi untuk dihubungkan:

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

ini harus menjadi jawabannya
Deen John

16

Saya akan mencoba menjawab pertanyaan asli ...

Komponen Cerdas & Bodoh

Dalam pertanyaan pertama Anda, pada dasarnya Anda bertanya mengapa bindActionCreatorspada awalnya diperlukan, dan jenis komponen apa yang tidak harus diperhatikan Redux.

Singkatnya, idenya di sini adalah bahwa komponen harus dipecah menjadi komponen pintar (wadah) dan bodoh (presentasi). Komponen bodoh bekerja atas dasar kebutuhan untuk mengetahui. Pekerjaan jiwa mereka adalah membuat data yang diberikan ke HTML dan tidak lebih. Mereka seharusnya tidak menyadari cara kerja aplikasi. Mereka dapat dilihat sebagai lapisan kulit bagian depan aplikasi Anda.

Di sisi lain, komponen pintar adalah semacam perekat, yang menyiapkan data untuk komponen bodoh dan lebih disukai tidak melakukan rendering HTML.

Jenis arsitektur ini mempromosikan penggabungan yang longgar antara lapisan UI dan lapisan data di bawahnya. Hal ini pada gilirannya memungkinkan penggantian yang mudah dari salah satu dari dua lapisan dengan yang lain (yaitu desain baru dari UI), yang tidak akan merusak lapisan lainnya.

Untuk menjawab pertanyaan Anda: komponen bodoh seharusnya tidak mengetahui Redux (atau detail implementasi lapisan data yang tidak perlu dalam hal ini) karena kami mungkin ingin menggantinya dengan yang lain di masa mendatang.

Anda dapat menemukan lebih banyak tentang konsep ini di manual Redux dan lebih mendalam dalam artikel Komponen Penyajian dan Kontainer oleh Dan Abramov.

Contoh mana yang lebih baik

Pertanyaan kedua adalah tentang keuntungan / kerugian dari contoh yang diberikan.

Dalam contoh pertama , pembuat tindakan didefinisikan dalam actionCreatorsfile / modul terpisah , yang berarti mereka dapat digunakan kembali di tempat lain. Ini adalah cara standar untuk mendefinisikan tindakan. Saya tidak benar-benar melihat kerugian dalam hal ini.

Contoh kedua mendefinisikan pembuat tindakan sebaris, yang memiliki banyak kelemahan:

  • pembuat tindakan tidak dapat digunakan kembali (jelas)
  • masalahnya lebih bertele-tele, yang diterjemahkan menjadi kurang terbaca
  • jenis tindakan dikodekan dengan keras - lebih baik jika didefinisikan secara conststerpisah, sehingga dapat direferensikan dalam reduksi - yang akan mengurangi kemungkinan kesalahan pengetikan
  • mendefinisikan pembuat tindakan sebaris bertentangan dengan cara penggunaan yang disarankan / diharapkan - yang akan membuat kode Anda sedikit kurang dapat dibaca oleh komunitas, jika Anda berencana untuk membagikan kode Anda

Contoh kedua memiliki satu keunggulan dibandingkan yang pertama - lebih cepat untuk menulis! Jadi, jika Anda tidak memiliki rencana yang lebih besar untuk kode Anda, itu mungkin baik-baik saja.

Saya harap saya bisa menjelaskan sedikit ...


2

Salah satu kegunaan yang mungkin bindActionCreators()adalah untuk "memetakan" banyak tindakan bersama-sama sebagai satu prop.

Pengiriman normal terlihat seperti ini:

Petakan beberapa tindakan pengguna yang umum ke properti.

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

Dalam proyek yang lebih besar, memetakan setiap pengiriman secara terpisah dapat terasa berat. Jika kami memiliki banyak tindakan yang terkait satu sama lain, kami dapat menggabungkan tindakan ini . Misalnya file tindakan pengguna yang melakukan semua jenis tindakan terkait pengguna yang berbeda. Alih-alih memanggil setiap tindakan sebagai dispatch terpisah kita dapat menggunakan bindActionCreators()bukan dispatch.

Beberapa Pengiriman menggunakan bindActionCreators ()

Impor semua tindakan terkait Anda. Semuanya mungkin ada di file yang sama di toko redux

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

Dan sekarang alih-alih menggunakan pengiriman, gunakan bindActionCreators ()

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

Sekarang saya dapat menggunakan prop userActionuntuk memanggil semua tindakan dalam komponen Anda.

YAITU: userAction.login() userAction.editEmail() atau this.props.userAction.login() this.props.userAction.editEmail().

CATATAN: Anda tidak perlu memetakan bindActionCreators () ke satu prop. (Tambahan => {return {}}yang memetakan ke userAction). Anda juga dapat menggunakan bindActionCreators()untuk memetakan semua tindakan dari satu file sebagai alat peraga terpisah. Tapi menurut saya, melakukan itu bisa membingungkan. Saya lebih suka setiap tindakan atau "kelompok tindakan" diberi nama yang jelas. Saya juga suka menamai the ownPropsagar lebih deskriptif tentang apa itu "alat peraga anak" ini atau dari mana asalnya. Saat menggunakan Redux + React, ini bisa menjadi sedikit membingungkan di mana semua properti disediakan sehingga semakin deskriptif semakin baik.


2

dengan menggunakan bindActionCreators, itu dapat mengelompokkan beberapa fungsi tindakan dan meneruskannya ke komponen yang tidak mengetahui Redux (Komponen Dumb) seperti itu

// 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

Saya juga ingin mengetahui lebih banyak tentang bindActionsCreators dan berikut adalah cara saya menerapkannya dalam proyek saya.

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

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

export default { loginRequest, logoutRequest };

Di Komponen React Anda

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

Salah satu kasus penggunaan yang bagus bindActionCreatorsadalah untuk integrasi dengan redux-saga menggunakan redux-saga-routine . Sebagai contoh:

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

Perhatikan bahwa pola ini dapat diimplementasikan menggunakan React Hooks (mulai dari React 16.8).


-1

The docs Pernyataan ini sangat jelas:

Satu-satunya kasus penggunaan bindActionCreatorsadalah ketika Anda ingin meneruskan beberapa pembuat tindakan ke komponen yang tidak mengetahui Redux, dan Anda tidak ingin meneruskan pengiriman atau penyimpanan Redux ke sana.

Ini jelas merupakan kasus penggunaan yang mungkin muncul dalam satu kondisi berikut dan hanya:

Katakanlah, kami memiliki komponen A dan B:

// 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

Menyuntikkan ke react-redux: (A)

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

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

Disuntikkan oleh react-redux: (B)

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

Memperhatikan hal-hal berikut?

  • Kami memperbarui penyimpanan redux melalui komponen A sementara kami tidak mengetahui penyimpanan redux di komponen B.

  • Kami tidak memperbarui di komponen A. Untuk mengetahui sebenarnya apa yang saya maksud, Anda dapat menjelajahi posting ini . Saya harap Anda punya ide.


7
Saya tidak mengerti apa
vsync
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.