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
'IT TIP' 카테고리의 다른 글
np.mean과 tf.reduce_mean의 차이점은 무엇입니까? (0) | 2020.10.14 |
---|---|
VueJS의 Angular Service에 해당하는 것은 무엇입니까? (0) | 2020.10.14 |
Ruby에서 모듈의 인스턴스 변수를 초기화하려면 어떻게해야합니까? (0) | 2020.10.14 |
Python의 스레드 내에서 호출 될 때 sys.exit ()가 종료되지 않는 이유는 무엇입니까? (0) | 2020.10.14 |
자식 클래스의 이름을 모르고 django에서 개체의 자식 클래스에 액세스하려면 어떻게해야합니까? (0) | 2020.10.14 |