bindActionCreators는 언제 react / redux에서 사용됩니까?


93

bindActionCreators에 대한 Redux 문서는 다음과 같이 설명합니다.

에 대한 유일한 사용 사례 bindActionCreators는 Redux를 인식하지 않는 구성 요소에 일부 작업 생성자를 전달하고 디스패치 또는 Redux 저장소를 전달하지 않으려는 경우입니다.

예를 들면 무엇입니까? bindActionCreators사용 / 필요한 ?

Redux를 인식하지 못하는 구성 요소 유형 합니까?

두 옵션의 장점 / 단점은 무엇입니까?

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

답변:


59

나는 가장 대중적인 대답이 실제로 질문을 다룬다 고 생각하지 않습니다.

아래의 모든 예제는 기본적으로 동일한 작업을 수행하고 "사전 바인딩"개념을 따르지 않습니다.

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


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


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

옵션 #3은 옵션 의 속기 일 뿐이 #1므로 옵션 #1대 옵션을 사용하는 진짜 질문 #2입니다. 나는 둘 다 react-redux 코드베이스에서 사용되는 것을 보았고 다소 혼란 스럽습니다.

나는 doc의 모든 예제react-redux사용 bindActionCreators하는 반면 bindActionCreators 의 doc (질문 자체에서 인용)은 react-redux와 함께 사용하지 말라고 혼란스러워 합니다.

나는 대답은 코드베이스의 일관성 것 같아요,하지만 난 개인적으로 명시 적으로 조치 포장 선호 파견을 필요할 때마다.


3
옵션 #3은 어떻게 옵션 의 속기 #1입니까?
ogostos


@ArtemBernatskyi 감사합니다. 이 삼가지 경우가 있습니다에서 그래서집니다 mapDispatchToProps: function, object누락. 각 케이스를 처리하는 방법은 여기
ogostos

나는이 대답을 찾고 있었다. 감사.
Kishan Rajdev

저는 실제로 React 문서에서 "일부 액션 생성자를 Redux를 인식하지 못하는 구성 요소로 전달" 하는 특정 사용 사례를 보았습니다 . 단순한 구성 요소를 더 복잡한 구성 요소에 연결하고 오버 헤드를 추가하기 때문입니다. 의 reduxconnectaddDispatchToProps난 그냥 하나 개의 소품 아래를 통과 할 경우 간단한 구성 요소가 과잉 것입니다. 그러나 내가이 알고 거의 모든 경우를 해요까지로 mapDispatch중 옵션이 될 것이다 소품에 #1또는 #3이 질문에 대해 언급
icc97

48

99 %의 connect()경우 mapDispatchToProps매개 변수의 일부로 React-Redux 기능 과 함께 사용됩니다 . mapDispatch사용자가 제공 하는 함수 내에서 명시 적으로 사용할 수도 있고 , 객체 축약 구문을 사용하고 액션 생성자로 가득 찬 객체를에 전달하는 경우 자동으로 사용할 수도 있습니다 connect.

아이디어는 액션 생성자를 미리 묶음으로써 전달되는 구성 요소가 connect()기술적으로 연결되어 있다는 것을 "모르는"다는 것입니다. 단지 실행해야한다는 것만 알고 있습니다 this.props.someCallback(). 반면에 액션 생성자를 바인드하지 않고를 호출 this.props.dispatch(someActionCreator())하면 이제 컴포넌트는 props.dispatch존재하기를 기대 하기 때문에 연결되었음을 "인식" 합니다.

내 블로그 게시물 Idiomatic Redux : Why use action creators? 에서이 주제에 대한 몇 가지 생각을 썼습니다 . .


1
그러나 그것은 'mapDispatchToProps'와 연결되어 있습니다. 바인딩 액션 생성자는 디버깅과 같은 다른 많은 것들 중에서 함수 정의 (TS 또는 Flow)를 잃어 버리기 때문에 실제로 부정적 / 무의미한 것 같습니다. 내 새로운 프로젝트에서 나는 그것을 사용하지 않았으며 지금까지 문제가 없었습니다. 또한 Saga를 사용하고 상태를 유지합니다. 또한 redux 함수를 호출하는 경우 (멋진 클릭이 발생합니다) 제 생각에는 지저분하고 무의미한 액션 제작자를 '첨부'하는 것이 더 깨끗하다고 ​​말할 것입니다. 스마트 (일반적으로 화면 구성 요소)는 여전히 소품에 대해서만 연결을 사용할 수 있습니다.
Oliver Dixon

19

보다 완전한 예는 연결할 액션 생성자로 가득 찬 객체를 전달하는 것입니다.

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

이 대답해야한다
디나 존

16

나는 원래의 질문에 대답하려고 노력할 것입니다 ...

Smart & Dumb 구성 요소

첫 번째 질문에서 기본적으로 왜 bindActionCreators필요한지, 그리고 어떤 종류의 구성 요소가 Redux를 인식하지 않아야하는지 묻습니다 .

요컨대 구성 요소는 스마트 (컨테이너) 및 멍청한 (프레젠테이션) 구성 요소 로 분리되어야한다는 것 입니다. 멍청한 구성 요소 는 알아야 할 필요에 따라 작동합니다. 그들의 영혼의 임무는 주어진 데이터를 HTML로만 렌더링하는 것입니다. 그들은 응용 프로그램의 내부 작동을 인식하지 않아야합니다. 응용 프로그램의 피부 깊숙한 전면 레이어로 볼 수 있습니다.

반면 스마트 구성 요소 는 일종의 접착제로, 멍청한 구성 요소에 대한 데이터를 준비 하고 HTML 렌더링을 수행하지 않는 것이 좋습니다.

이러한 종류의 아키텍처는 UI 계층과 그 아래에있는 데이터 계층 간의 느슨한 결합을 촉진합니다. 이것은 차례로 두 레이어 중 하나를 다른 레이어 (예 : 새로운 UI 디자인)로 쉽게 교체 할 수있게하여 다른 레이어를 손상시키지 않습니다.

귀하의 질문에 답하기 위해 : 멍청한 구성 요소는 Redux (또는 해당 문제에 대한 데이터 계층의 불필요한 구현 세부 사항)를 인식하지 않아야합니다. 왜냐하면 나중에 다른 것으로 교체하고 싶을 수 있기 때문입니다.

이 개념에 대한 자세한 내용은 Redux 매뉴얼 과 Dan Abramov의 Presentational and Container Components 기사에서 더 자세히 확인할 수 있습니다 .

어떤 예가 더 낫다

두 번째 질문은 주어진 예제의 장점 / 단점에 관한 것이 었습니다.

에서 첫 번째 예 작업 제작자는 별도의 정의 actionCreators가 다른 재사용 될 수 있다는 것을 의미하는, 파일 / 모듈. 행동을 정의하는 표준적인 방법입니다. 나는 이것에 어떤 단점도 보이지 않는다.

번째 예 는 작업 작성자를 인라인으로 정의하며 여러 가지 단점이 있습니다.

  • 액션 제작자는 재사용 할 수 없습니다 (분명히)
  • 문제가 더 장황하여 읽기가 어렵습니다.
  • 액션 유형은 하드 코딩 consts되어 있습니다. 리듀서에서 참조 할 수 있도록 개별적 으로 정의하는 것이 좋습니다 . 그러면 입력 실수를 줄일 수 있습니다.
  • 액션 생성자를 인라인으로 정의하는 것은 권장 / 예상되는 사용 방법에 위배됩니다. 따라서 코드를 공유하려는 경우 커뮤니티에서 코드를 읽을 수 없게됩니다.

두 번째 예제는 첫 번째 예제 보다 한 가지 장점 이 있습니다. 작성하는 것이 더 빠릅니다! 따라서 코드에 대한 더 큰 계획이 없다면 괜찮을 수 있습니다.

나는 내가 일을 조금 명확히 할 수 있기를 바랍니다 ...


2

한 가지 가능한 용도는 bindActionCreators()여러 액션을 하나의 소품으로 함께 "매핑"하는 것입니다.

일반적인 디스패치는 다음과 같습니다.

몇 가지 일반적인 사용자 작업을 소품에 매핑합니다.

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

대규모 프로젝트에서 각 디스패치를 ​​개별적으로 매핑하는 것은 다루기 힘들 수 있습니다. 서로 관련된 많은 작업이있는 경우 이러한 작업을 결합 할 수 있습니다 . 예를 들어 모든 종류의 다른 사용자 관련 작업을 수행 한 사용자 작업 파일입니다. 각 액션을 별도의 디스패치로 호출하는 bindActionCreators()대신 dispatch.

bindActionCreators ()를 사용한 다중 디스패치

모든 관련 작업을 가져옵니다. redux 저장소의 동일한 파일에있을 가능성이 높습니다.

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

이제 디스패치를 ​​사용하는 대신 bindActionCreators ()를 사용하십시오.

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

이제 소품 userAction을 사용 하여 구성 요소의 모든 작업을 호출 할 수 있습니다 .

IE : userAction.login() userAction.editEmail() 또는 this.props.userAction.login() this.props.userAction.editEmail().

참고 : bindActionCreators ()를 단일 소품에 매핑 할 필요는 없습니다. ( => {return {}}에 매핑 되는 추가 userAction). 를 사용 bindActionCreators()하여 단일 파일의 모든 작업을 별도의 소품으로 매핑 할 수도 있습니다 . 하지만 그렇게하는 것이 혼란 스러울 수 있습니다. 각 작업 또는 "작업 그룹"에 명시적인 이름을 지정하는 것을 선호합니다. 또한 ownProps이 "하위 소품"이 무엇인지 또는 어디에서 왔는지에 대해 더 설명하기 위해의 이름을 지정하고 싶습니다 . Redux + React를 사용할 때 모든 소품이 제공되는 위치가 약간 혼란스러워서 설명이 많을수록 좋습니다.


2

를 사용하여 bindActionCreators여러 액션 함수를 그룹화 하여 Redux (Dumb Component)를 인식하지 못하는 컴포넌트에 다음과 같이 전달할 수 있습니다.

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

또한 bindActionsCreators 에 대해 더 많이 알고 싶었고 여기에 내 프로젝트에서 구현 한 방법이 있습니다.

// 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 컴포넌트에서

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

에 대한 하나의 좋은 사용 사례 bindActionCreators와의 통합을위한 REDUX - 사가 사용 REDUX-사가 루틴을 . 예를 들면 :

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

이 패턴은 React Hooks (React 16.8부터)를 사용하여 구현할 수 있습니다 .


-1

문서의 문은 매우 분명하다 :

에 대한 유일한 사용 사례 bindActionCreators는 Redux를 인식하지 않는 구성 요소에 일부 작업 생성자를 전달하고 디스패치 또는 Redux 저장소를 전달하지 않으려는 경우입니다.

이는 다음과 같은 조건에서만 발생할 수있는 사용 사례입니다.

컴포넌트 A와 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

react-redux에 주입 : (A)

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

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

react-redux에 의해 주입 됨 : (B)

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

다음을 알고 계십니까?

  • 우리는 컴포넌트 B의 redux 스토어를 알지 못했지만 컴포넌트 A를 통해 redux 스토어를 업데이트했습니다.

  • 컴포넌트 A에서 업데이트하지 않습니다. 정확히 무슨 뜻인지 알기 위해이 게시물을 살펴볼 수 있습니다 . 나는 당신이 아이디어가 있기를 바랍니다.


7
난 아무것도 이해하지 못했다
수직 동기화
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.