IT TIP

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

itqueen 2020. 10. 14. 21:30
반응형

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

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

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

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


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

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

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

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


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

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여러 액션 함수를 그룹화 하여 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>
  }
}

에 대한 하나의 좋은 사용 사례 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부터)를 사용하여 구현할 수 있습니다 .


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

한 가지 가능한 용도는 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 ()를 사용한 다중 디스패치

Import all your related actions. They are likely all in the same file in the redux store

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

And now instead of using dispatch use bindActionCreators()

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

Now I can use the prop userAction to call all the actions in your component.

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

NOTE: You do not have to map the bindActionCreators() to a single prop. (The additional => {return {}} that maps to userAction). You can also use bindActionCreators() to map all the actions of a single file as separate props. But I find doing that can be confusing. I prefer having each action or "action group" be given an explicit name. I also like to name the ownProps to be more descriptive about what these "child props" are or where they are coming from. When using Redux + React it can get a bit confusing where all the props are being supplied so the more descriptive the better.


The docs statement is very clear:

The only use case for bindActionCreators is when you want to pass some action creators down to a component that isn't aware of Redux, and you don't want to pass dispatch or the Redux store to it.

This is clearly a use case that may arise in the following and only one condition:

Lets say, we have component A and 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

Inject to react-redux: (A)

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

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

Injected by react-redux: (B)

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

Noticed of the following?

  • We updated the redux store through the component A whilst we were unknown of redux store in component B.

  • We're not updating in component A. To know what exactly I mean, you may explore this post. I hope you'll have an idea.

참고URL : https://stackoverflow.com/questions/41754489/when-would-bindactioncreators-be-used-in-react-redux

반응형