액션 크리에이터에서 Redux 상태에 액세스하고 있습니까?


296

내가 다음을 가지고 있다고 가정 해보십시오.

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return {
    type: SOME_ACTION,
  }
}

그리고 그 액션 크리에이터에서 글로벌 스토어 상태 (모든 리듀서)에 액세스하고 싶습니다. 이것을하는 것이 낫습니다 :

import store from '../store';

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return {
    type: SOME_ACTION,
    items: store.getState().otherReducer.items,
  }
}

아니면 이거:

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return (dispatch, getState) => {
    const {items} = getState().otherReducer;

    dispatch(anotherAction(items));
  }
}

답변:


522

활동중인 작성자의 상태에 액세스하는 것이 좋은 아이디어인지에 대해 다른 의견이 있습니다.

  • Redux 제작자 Dan Abramov는 다음과 같이 제한적이라고 생각합니다. "허용되는 것으로 생각되는 몇 가지 사용 사례는 요청하기 전에 캐시 된 데이터를 확인하거나 인증 여부 (즉, 조건부 디스패치)를 확인하는 것입니다. 나는 지나가는 생각 데이터state.something.items이 버그와 경우 : 액션 작성자의 것은 확실히 안티 패턴이며이 변경 기록을 가려 때문에 권장하지 않습니다 items잘못, 그것을 추적하기 어려운 경우 그 잘못된 값들이 있기 때문에 출신 조치에 대한 응답으로 감속기에 의해 직접 계산되는 것이 아니라 이미 조치의 일부입니다. "
  • 현재의 Redux 관리자 인 마크 에릭슨 (Mark Erikson)은이 기능이 훌륭하고 getState썽크 에 사용하도록 권장 한다고 말합니다 . 그는 블로그 게시물 Idiomatic Redux : Thunks, Sagas, Abstraction, and Reusability에 대한 생각 에서 액션 제작자 상태 액세스의 장단점에 대해 설명합니다 .

이것이 필요하다는 것을 알게되면 제안한 두 가지 접근법이 모두 좋습니다. 첫 번째 접근 방식에는 미들웨어가 필요하지 않습니다.

import store from '../store';

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return {
    type: SOME_ACTION,
    items: store.getState().otherReducer.items,
  }
}

그러나 store일부 모듈에서 내 보낸 싱글 톤 에 의존한다는 것을 알 수 있습니다 . 대부분의 경우 서버마다 요청마다 별도의 저장소를 원하기 때문에 앱에 서버 렌더링추가 하는 것이 훨씬 어렵 기 때문에 권장하지 않습니다 . 따라서이 방법이 기술적으로 작동하는 동안 모듈에서 상점을 내보내는 것은 권장하지 않습니다.

이것이 두 번째 접근법을 권장하는 이유입니다.

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return (dispatch, getState) => {
    const {items} = getState().otherReducer;

    dispatch(anotherAction(items));
  }
}

Redux Thunk 미들웨어를 사용해야하지만 클라이언트와 서버 모두에서 잘 작동합니다. Redux Thunk에 대한 자세한 내용 과이 경우 필요한지 여기를 참조하십시오 .

이상적으로는, 당신의 행동이 "뚱뚱"해서는 안되며 가능한 적은 정보를 포함해야하지만, 자신의 응용 프로그램에서 가장 적합한 것을 자유롭게 할 수 있습니다. 돌아 오는 자주 묻는 질문에 대한 정보가 액션 제작자와 감속기 사이의 분할 논리그것을 사용하는 것이 유용 할 수 있습니다 때 시간을 getState액션 창조자에서 .


5
구성 요소에서 무언가를 선택하면 저장소에 구성 요소와 관련된 데이터가 포함되어 있는지 여부에 따라 PUT 또는 POST가 트리거 될 수 있습니다. 썽크 기반 액션 제작자 대신 PUT / POST 선택에 대한 비즈니스 로직을 컴포넌트에 배치하는 것이 더 낫습니까?
vicusbass

3
모범 사례는 무엇입니까? 이제 액션 제작자에서 getState를 사용하는 비슷한 문제에 직면하고 있습니다. 필자의 경우 양식에서 변경 사항이 보류 중인지 여부를 확인하고 대화 상자를 표시하는 작업을 전달합니다.
Arne H. Bitubekk

42
Action Creator의 상점에서 읽는 것이 좋습니다. 정확한 상태 모양에 의존하지 않도록 선택기사용하는 것이 좋습니다 .
Dan Abramov

2
미들웨어를 사용하여 데이터를 mixpanel에 보냅니다. 그래서 액션 안에 메타 키가 있습니다. 상태에서 믹스 패널로 다른 변수를 전달해야합니다. 액션 제작자에게 설정하는 것은 반 패턴으로 보입니다. 이러한 종류의 사용 사례를 처리하는 가장 좋은 방법은 무엇입니까?
Aakash Sigdel

2
오! 나는 redux 썽크가 getState두 번째 매개 변수로 받는 매듭을하지 않았다 , 나는 머리를 부수고 너무 감사합니다
Lai32290

33

시나리오가 간단하면 사용할 수 있습니다

import store from '../store';

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return {
    type: SOME_ACTION,
    items: store.getState().otherReducer.items,
  }
}

그러나 때로는 action creator여러 행동을 유발해야

예를 들어 비동기 요청이므로 REQUEST_LOAD REQUEST_LOAD_SUCCESS REQUEST_LOAD_FAIL조치 가 필요합니다.

export const [REQUEST_LOAD, REQUEST_LOAD_SUCCESS, REQUEST_LOAD_FAIL] = [`REQUEST_LOAD`
    `REQUEST_LOAD_SUCCESS`
    `REQUEST_LOAD_FAIL`
]
export function someAction() {
    return (dispatch, getState) => {
        const {
            items
        } = getState().otherReducer;
        dispatch({
            type: REQUEST_LOAD,
            loading: true
        });
        $.ajax('url', {
            success: (data) => {
                dispatch({
                    type: REQUEST_LOAD_SUCCESS,
                    loading: false,
                    data: data
                });
            },
            error: (error) => {
                dispatch({
                    type: REQUEST_LOAD_FAIL,
                    loading: false,
                    error: error
                });
            }
        })
    }
}

참고 : 액션 제작자에서 함수를 반환 하려면 redux-thunk 가 필요합니다 .


3
첫 번째 요청이 완료되는 동안 다른 아약스 요청이 이루어지지 않도록 상태 '로드 중'의 상태를 확인해야하는지 물어볼 수 있습니까?
JoeTidee

@JoeTidee이 예에서는로드 상태 디스패치가 수행됩니다. 예를 들어 버튼으로이 작업을 수행하면 버튼이 있는지 확인 loading === true하고 버튼을 비활성화합니다.
croraf

4

@Bloomca에 동의합니다. 상점에서 필요한 값을 전달 기능으로 인수로 전달하는 것은 상점을 내보내는 것보다 간단합니다. 나는 여기에 예를 만들었다.

import React from "react";
import {connect} from "react-redux";
import * as actions from '../actions';

class App extends React.Component {

  handleClick(){
    const data = this.props.someStateObject.data;
    this.props.someDispatchFunction(data);
  }

  render(){
    return (
      <div>       
      <div onClick={ this.handleClick.bind(this)}>Click Me!</div>      
      </div>
    );
  }
}


const mapStateToProps = (state) => {
  return { someStateObject: state.someStateObject };
};

const mapDispatchToProps = (dispatch) => {
  return {
    someDispatchFunction:(data) => { dispatch(actions.someDispatchFunction(data))},

  };
}


export default connect(mapStateToProps, mapDispatchToProps)(App);

이 방법은 매우 논리적입니다.
Ahmet Şimşek

이것이 올바른 방법과 내가하는 방법. 액션 제작자는 전체 상태를 알 필요가 없으며 관련된 상태 만 알 수 있습니다.
애쉬

3

상점에서 읽는 것이 그렇게 나쁘지는 않다는 점을 지적하고 싶습니다. 모든 것을 구성 요소에 전달 한 다음 매개 변수로 저장하는 것보다 상점을 기반으로 수행해야 할 작업을 결정하는 것이 훨씬 더 편리 할 수 ​​있습니다 기능. 클라이언트 측 렌더링에만 사용한다고 100 % 확신하지 않는 한 (단, 버그 추적이 어려울 수 있음) 단단하지 않은 경우 상점을 단일 톤으로 사용하지 않는 것이 훨씬 낫다는 Dan의 의견에 전적으로 동의합니다.

최근에 redux의 세부 정보를 다루기 위해 라이브러리를 만들었 으며 미들웨어에 모든 것을 넣는 것이 좋습니다.

따라서 귀하의 예는 다음과 같습니다.

import { createSyncTile } from 'redux-tiles';

const someTile = createSyncTile({
  type: ['some', 'tile'],
  fn: ({ params, selectors, getState }) => {
    return {
      data: params.data,
      items: selectors.another.tile(getState())
    };
  },
});

그러나 보시다시피 여기서 데이터를 실제로 수정하지는 않으므로 다른 곳 에서이 선택기를 사용하여 다른 곳에서 결합 할 수있는 좋은 기회가 있습니다.


1

이를 해결하는 다른 방법을 제시합니다. 응용 프로그램에 따라 Dan의 솔루션보다 낫거나 나쁠 수 있습니다.

액션을 두 개의 개별 기능으로 분할하여 리듀서에서 상태를 액션으로 가져올 수 있습니다. 먼저 데이터 요청, 두 번째 데이터 작업. 를 사용하여 그렇게 할 수 있습니다 redux-loop.

먼저 '친절하게 데이터를 요청하십시오'

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
    return {
        type: SOME_ACTION,
    }
}

감속기에서 요청을 가로 채고를 사용하여 데이터를 2 단계 조치에 제공하십시오 redux-loop.

import { loop, Cmd } from 'redux-loop';
const initialState = { data: '' }
export default (state=initialState, action) => {
    switch(action.type) {
        case SOME_ACTION: {
            return loop(state, Cmd.action(anotherAction(state.data))
        }
    }
}

손에 든 데이터를 사용하여 처음에 원하는 것을 수행하십시오.

export const ANOTHER_ACTION = 'ANOTHER_ACTION';
export function anotherAction(data) {
    return {
        type: ANOTHER_ACTION,
        payload: data,
    }
}

이것이 누군가를 돕기를 바랍니다.


0

나는 여기서 파티에 늦었다는 것을 알고 있지만, 행동에 국가를 사용하려는 내 자신의 욕구에 대한 의견을 여기에 왔으며, 내가 올바른 행동이라고 생각하는 것을 깨달았을 때 내 자신을 형성했습니다.

이것은 선택자가 나에게 가장 적합한 곳입니다. 이 요청을 발행 한 구성 요소는 선택을 통해 발행 할시기가되어야합니다.

export const SOME_ACTION = 'SOME_ACTION';
export function someAction(items) {
  return (dispatch) => {
    dispatch(anotherAction(items));
  }
}

추상화가 유출되는 것처럼 느껴질 수 있지만 구성 요소는 메시지를 명확하게 보내야하며 메시지 페이로드에는 관련 상태가 포함되어야합니다. 불행히도 귀하의 질문에는 선택기 및 동작의 '더 나은 모델'을 사용할 수 있기 때문에 구체적인 예가 없습니다.


0

나는 가장 깨끗한 것을 찾을 수있는 또 다른 대안을 제안하고 싶지만, react-redux또는 비슷한 것이 필요합니다. 또한 그 과정에서 다른 멋진 기능을 사용하고 있습니다.

// actions.js
export const someAction = (items) => ({
    type: 'SOME_ACTION',
    payload: {items},
});
// Component.jsx
import {connect} from "react-redux";

const Component = ({boundSomeAction}) => (<div
    onClick={boundSomeAction}
/>);

const mapState = ({otherReducer: {items}}) => ({
    items,
});

const mapDispatch = (dispatch) => bindActionCreators({
    someAction,
}, dispatch);

const mergeProps = (mappedState, mappedDispatches) => {
    // you can only use what gets returned here, so you dont have access to `items` and 
    // `someAction` anymore
    return {
        boundSomeAction: () => mappedDispatches.someAction(mappedState.items),
    }
});

export const ConnectedComponent = connect(mapState, mapDispatch, mergeProps)(Component);
// (with  other mapped state or dispatches) Component.jsx
import {connect} from "react-redux";

const Component = ({boundSomeAction, otherAction, otherMappedState}) => (<div
    onClick={boundSomeAction}
    onSomeOtherEvent={otherAction}
>
    {JSON.stringify(otherMappedState)}
</div>);

const mapState = ({otherReducer: {items}, otherMappedState}) => ({
    items,
    otherMappedState,
});

const mapDispatch = (dispatch) => bindActionCreators({
    someAction,
    otherAction,
}, dispatch);

const mergeProps = (mappedState, mappedDispatches) => {
    const {items, ...remainingMappedState} = mappedState;
    const {someAction, ...remainingMappedDispatch} = mappedDispatch;
    // you can only use what gets returned here, so you dont have access to `items` and 
    // `someAction` anymore
    return {
        boundSomeAction: () => someAction(items),
        ...remainingMappedState,
        ...remainingMappedDispatch,
    }
});

export const ConnectedComponent = connect(mapState, mapDispatch, mergeProps)(Component);

당신이 다시 사용하려는 경우 특정을 추출해야합니다 mapState, mapDispatch그리고 mergeProps기능에 다른 곳에서 재사용 할 수 있지만,이 차종은 완벽하게 명확한 종속성.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.