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

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



답변

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

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

// 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와 함께 사용하지 말라고 혼란스러워 합니다.

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


답변

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

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

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


답변

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

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


답변

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

Smart & Dumb 구성 요소

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

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

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

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

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

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

어떤 예가 더 낫다

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

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

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

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

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

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


답변

한 가지 가능한 용도는 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를 사용할 때 모든 소품이 제공되는 위치가 약간 혼란스러워서 설명이 많을수록 좋습니다.


답변

를 사용하여 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>
  }
}


답변

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