source

시간 초과 시 Redux 작업을 디스패치하는 방법

manysource 2022. 11. 3. 21:57

시간 초과 시 Redux 작업을 디스패치하는 방법

응용 프로그램의 알림 상태를 업데이트하는 작업이 있습니다.일반적으로 이 알림은 오류 또는 정보입니다.그런 다음 5초 후에 다른 액션을 디스패치해야 합니다.이 액션은 알림 상태를 원래 상태로 되돌리기 때문에 알림은 없습니다.그 주된 이유는 5초 후에 알림이 자동으로 사라지는 기능을 제공하기 위해서입니다.

나는 그것을 사용할 운이 없었다.setTimeout다른 액션을 반환하고 있지만 온라인에서는 이 작업을 수행할 수 없습니다.그래서 어떤 조언이라도 환영한다.

도서관이 모든 것을 어떻게 해야 하는지를 규정해야 한다고 생각하는 함정에 빠지지 마세요.JavaScript에서 타임아웃으로 작업을 수행하려면setTimeout 할 는 없습니다. 환원하다

Redux는 비동기적인 것을 처리하는 몇 가지 대체 방법을 제공하지만, 너무 많은 코드를 반복하고 있다는 것을 깨달았을 때만 이러한 방법을 사용해야 합니다.이 문제가 없는 한 언어에서 제공하는 것을 사용하여 가장 간단한 해결 방법을 선택하십시오.

비동기 코드 인라인 쓰기

이것이 단연코 가장 간단한 방법이다.여기에는 Redux에 대한 특별한 내용은 없습니다.

store.dispatch({ type: 'SHOW_NOTIFICATION', text: 'You logged in.' })
setTimeout(() => {
  store.dispatch({ type: 'HIDE_NOTIFICATION' })
}, 5000)

마찬가지로 접속되어 있는 컴포넌트 내부에서도 다음 작업을 수행합니다.

this.props.dispatch({ type: 'SHOW_NOTIFICATION', text: 'You logged in.' })
setTimeout(() => {
  this.props.dispatch({ type: 'HIDE_NOTIFICATION' })
}, 5000)

자체에 수 쪽인가로 할 수 입니다.dispatch()이것은 아무런 가 없다.하지만 이것은 우리에게 아무런 차이가 없다.

서로 다른 컴포넌트에서 동일한 액션을 디스패치할 때 오타가 나지 않는 경우 액션개체를 인라인으로 디스패치하는 대신 액션크리에이터를 추출할 수 있습니다.

// actions.js
export function showNotification(text) {
  return { type: 'SHOW_NOTIFICATION', text }
}
export function hideNotification() {
  return { type: 'HIDE_NOTIFICATION' }
}

// component.js
import { showNotification, hideNotification } from '../actions'

this.props.dispatch(showNotification('You just logged in.'))
setTimeout(() => {
  this.props.dispatch(hideNotification())
}, 5000)

이전에 는는음음음음음음음음음음음음음 them them them them them 로 묶은 경우connect():

this.props.showNotification('You just logged in.')
setTimeout(() => {
  this.props.hideNotification()
}, 5000)

지금까지는 미들웨어나 다른 고급 개념을 사용하지 않았습니다.

비동기 작업 생성기 추출

위의 접근방식은 간단한 경우에는 정상적으로 동작하지만 몇 가지 문제가 있을 수 있습니다.

  • 이 로직은 통지를 표시하는 임의의 장소에 복제하도록 강제됩니다.
  • 알림에 ID가 없기 때문에 알림이 2개만 빨리 표시되면 레이스 상태가 됩니다.되면 이 됩니다.HIDE_NOTIFICATION타임아웃 후 바로 두 번째 알림을 잘못 숨깁니다.

이러한 문제를 해결하려면 타임아웃 로직을 일원화하여 이들2가지 액션을 디스패치하는 함수를 추출해야 합니다.다음과 같은 경우가 있습니다.

// actions.js
function showNotification(id, text) {
  return { type: 'SHOW_NOTIFICATION', id, text }
}
function hideNotification(id) {
  return { type: 'HIDE_NOTIFICATION', id }
}

let nextNotificationId = 0
export function showNotificationWithTimeout(dispatch, text) {
  // Assigning IDs to notifications lets reducer ignore HIDE_NOTIFICATION
  // for the notification that is not currently visible.
  // Alternatively, we could store the timeout ID and call
  // clearTimeout(), but we’d still want to do it in a single place.
  const id = nextNotificationId++
  dispatch(showNotification(id, text))

  setTimeout(() => {
    dispatch(hideNotification(id))
  }, 5000)
}

으로 컴포넌트는 「」를 사용할 수 되었습니다.showNotificationWithTimeout알림이 경우:

// component.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged in.')

// otherComponent.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged out.')    

?는 왜?showNotificationWithTimeout()dispatch첫째째 쟁쟁?스토어에 액션을 발송해야 하기 때문입니다.는 통통음 에 액세스 할 수 .dispatch하지만 디스패치를 제어하는 외부 기능을 원하기 때문에 디스패치를 제어할 수 있도록 해야 합니다.

Import하기만 .dispatch을 사용하다

// store.js
export default createStore(reducer)

// actions.js
import store from './store'

// ...

let nextNotificationId = 0
export function showNotificationWithTimeout(text) {
  const id = nextNotificationId++
  store.dispatch(showNotification(id, text))

  setTimeout(() => {
    store.dispatch(hideNotification(id))
  }, 5000)
}

// component.js
showNotificationWithTimeout('You just logged in.')

// otherComponent.js
showNotificationWithTimeout('You just logged out.')    

이 방법은 간단해 보이지만방법은 권장하지 않습니다.우리가 그것을 싫어하는 주된 이유는 그것이 가게를 독신자로 만들도록 강요하기 때문이다.따라서 서버 렌더링을 구현하기가 매우 어렵습니다.서버에서는, 각 요구에 독자적인 스토어를 갖게 해, 유저 마다 다른 프리 로드 데이터를 취득할 수 있도록 합니다.

싱글톤 가게는 또한 테스트를 더 어렵게 만든다.작업 작성자는 특정 모듈에서 내보낸 특정 실제 저장소를 참조하므로 작업 작성자를 테스트할 때 더 이상 저장소를 모의할 수 없습니다.외부에서도 리셋할 수 없어요

따라서 기술적으로는 모듈에서 싱글톤스토어를 내보낼 수 있지만 권장하지 않습니다.앱이 서버 렌더링을 추가하지 않을 것으로 확신하는 경우가 아니면 이 작업을 수행하지 마십시오.

이전 버전으로 되돌아가기:

// actions.js

// ...

let nextNotificationId = 0
export function showNotificationWithTimeout(dispatch, text) {
  const id = nextNotificationId++
  dispatch(showNotification(id, text))

  setTimeout(() => {
    dispatch(hideNotification(id))
  }, 5000)
}

// component.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged in.')

// otherComponent.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged out.')    

이것은 논리의 중복 문제를 해결하고 인종적 상황으로부터 우리를 구한다.

퉁크 미들웨어

단순한 앱의 경우 접근법으로도 충분합니다.미들웨어가 마음에 든다면 걱정하지 마세요.

그러나 대형 앱에서는 몇 가지 불편함을 느낄 수 있습니다.

를 들어,가 '', '아쉽다', ''를 통과해야 하는 것 요.dispatch주위에.위의 방법으로 Redx 액션을 비동기적으로 디스패치하는 컴포넌트는 모두 수용해야 하기 때문에 컨테이너 컴포넌트와 프레젠테이션 컴포넌트를 분리하는 것이 더 까다로워집니다.dispatch버팀목 역할을 하고 있어요.를 단순히 크리에이터와 수는 .connect() ★★★★★★★★★★★★★★★★★showNotificationWithTimeout()실제로 액션 크리에이터는 아닙니다.환원하다.

어떤 가 동기 수 .showNotification()입니다.showNotificationWithTimeout()서로 다르게 사용해야 하며 서로 오해하지 않도록 주의해야 합니다.

이는 도우미 기능에 제공하는 이러한 패턴을 "합법화"하고, Redux가 비동기식 액션 크리에이터를 완전히 다른 기능이 아닌 일반 액션 크리에이터의 특수한 경우로 볼 수 있도록 지원하는 방법을 찾는 동기가 되었습니다.

아직 저희 회사에 있고 앱에서도 문제가 있다고 생각하신다면 Redux Thunk 미들웨어를 사용하셔도 좋습니다.

요약하자면, Redx Thunk는 Redx에게 실제로 기능하는 특별한 종류의 동작을 인식하도록 가르칩니다.

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

const store = createStore(
  reducer,
  applyMiddleware(thunk)
)

// It still recognizes plain object actions
store.dispatch({ type: 'INCREMENT' })

// But with thunk middleware, it also recognizes functions
store.dispatch(function (dispatch) {
  // ... which themselves may dispatch many times
  dispatch({ type: 'INCREMENT' })
  dispatch({ type: 'INCREMENT' })
  dispatch({ type: 'INCREMENT' })

  setTimeout(() => {
    // ... even asynchronously!
    dispatch({ type: 'DECREMENT' })
  }, 1000)
})

이 미들웨어가 활성화되어 있는 경우 함수를 디스패치하면 Redx Thunk 미들웨어는 이 기능을dispatch 함수 인수를 .또한 이러한 동작을 "삼킬" 수 있으므로 감소기가 이상한 함수 인수를 받을 염려가 없습니다.리듀서는 직접 방출되거나 방금 설명한 대로 함수에 의해 방출되는 단순한 객체 작업만 받습니다.

건건별 로용 ?용 ?? ???단, 선언할 수 .showNotificationWithTimeout()리덕스:

// actions.js
function showNotification(id, text) {
  return { type: 'SHOW_NOTIFICATION', id, text }
}
function hideNotification(id) {
  return { type: 'HIDE_NOTIFICATION', id }
}

let nextNotificationId = 0
export function showNotificationWithTimeout(text) {
  return function (dispatch) {
    const id = nextNotificationId++
    dispatch(showNotification(id, text))

    setTimeout(() => {
      dispatch(hideNotification(id))
    }, 5000)
  }
}

이 함수는 이전 섹션에서 설명한 함수와 거의 동일합니다. 않습니다.dispatch첫 번째 인수로.대신, 이 함수는 다음과 같이 수신하는 함수를 반환합니다.dispatch첫 번째 인수로.

컴포넌트에서는 어떻게 사용합니까?물론, 다음과 같이 쓸 수 있습니다.

// component.js
showNotificationWithTimeout('You just logged in.')(this.props.dispatch)

하여 '비동기 액션크리에이터'만.dispatch 통과합니다.dispatch.

하지만 이것은 원래 버전보다 더 어색하다!왜 저쪽으로 갔지?

내가 전에 말했던 것 때문에.Redx Thunk 미들웨어가 네이블로 되어 있는 경우 액션오브젝트 대신 함수를 디스패치하려고 할 때마다 미들웨어는 메서드 자체를 첫 번째 인수로 하여 함수를 호출합니다.

대신 이 작업을 수행할 수 있습니다.

// component.js
this.props.dispatch(showNotificationWithTimeout('You just logged in.'))

마지막으로 비동기 액션(실제로 일련의 액션)을 디스패치하는 것은 컴포넌트에 동기적으로 단일 액션을 디스패치하는 것과 다를 바 없습니다.컴포넌트는 어떤 일이 동기적으로 일어나든 비동기적으로 일어나든 상관하지 않아야 하기 때문에 좋습니다.우리는 그것을 추상화했다.

Redx가 이러한 「특별한」액션 크리에이터(Tunk Action Creator)를 인식하도록 「교육」했기 때문에, 일반 액션 크리에이터를 사용하는 장소라면 어디에서라도 사용할 수 있게 되었습니다.예를 들어 다음과 같이 사용할 수 있습니다.connect():

// actions.js

function showNotification(id, text) {
  return { type: 'SHOW_NOTIFICATION', id, text }
}
function hideNotification(id) {
  return { type: 'HIDE_NOTIFICATION', id }
}

let nextNotificationId = 0
export function showNotificationWithTimeout(text) {
  return function (dispatch) {
    const id = nextNotificationId++
    dispatch(showNotification(id, text))

    setTimeout(() => {
      dispatch(hideNotification(id))
    }, 5000)
  }
}

// component.js

import { connect } from 'react-redux'

// ...

this.props.showNotificationWithTimeout('You just logged in.')

// ...

export default connect(
  mapStateToProps,
  { showNotificationWithTimeout }
)(MyComponent)

상태 읽기(Thunks)

일반적으로 리덕터에는 다음 상태를 판단하기 위한 비즈니스 로직이 포함되어 있습니다.단, 리듀서는 액션이 디스패치된 후에만 효과를 발휘됩니다.thunk 액션 크리에이터에서 부작용(API 호출 등)이 발생하여 어떤 상황에서 이를 방지하려면 어떻게 해야 합니까?

Thunk 미들웨어를 사용하지 않고 컴포넌트 내부에서 다음 검사를 수행합니다.

// component.js
if (this.props.areNotificationsEnabled) {
  showNotificationWithTimeout(this.props.dispatch, 'You just logged in.')
}

그러나 액션 크리에이터를 추출하는 목적은 이 반복적인 논리를 많은 컴포넌트에 걸쳐 일원화하는 것이었습니다.다행히 Redux Thunk는 Redux 스토어의 현재 상태를 읽을 수 있는 방법을 제공합니다.에 더하여dispatch , , , , , , , .getState펑크 액션 크리에이터에서 반환하는 함수의 두 번째 인수입니다.이를 통해 Thunk는 스토어의 현재 상태를 읽을 수 있습니다.

let nextNotificationId = 0
export function showNotificationWithTimeout(text) {
  return function (dispatch, getState) {
    // Unlike in a regular action creator, we can exit early in a thunk
    // Redux doesn’t care about its return value (or lack of it)
    if (!getState().areNotificationsEnabled) {
      return
    }

    const id = nextNotificationId++
    dispatch(showNotification(id, text))

    setTimeout(() => {
      dispatch(hideNotification(id))
    }, 5000)
  }
}

이 패턴을 악용하지 마세요.캐시된 데이터가 있을 때 API 호출을 해제하는 것은 좋지만, 비즈니스 로직을 구축하는 데는 그다지 좋은 기반이 되지 않습니다.「 」를 사용하고 getState()조건부로 다른 액션을 디스패치하는 경우에만 비즈니스 로직을 리듀서에 추가하는 것을 검토해 주십시오.

다음 단계

이제 Thunks의 작동 방식에 대한 기본적인 직관을 알게 되었습니다. 이제 Thunks를 사용하는 Redux 비동기 예를 확인하십시오.

당신은 thunks가 약속을 반환하는 많은 예를 찾을 수 있을 것이다.이것은 필수는 아니지만 매우 편리할 수 있습니다. Redx에서 되는 값을 합니다.dispatch()하고 Promise를 호출하여 수 dispatch(someThunkReturningPromise()).then(...).

복잡한 펑크 액션 크리에이터를 여러 개의 작은 펑크 액션 크리에이터로 분할할 수도 있습니다.dispatchthunks가 제공하는 메서드는 thunks 자체를 받아들일 수 있으므로 패턴을 재귀적으로 적용할 수 있습니다.이 경우에도 비동기 제어 플로우를 구현할 수 있기 때문에 Promise에 가장 적합합니다.

일부 앱의 경우 비동기 제어 흐름 요구사항이 너무 복잡하여 thunks로 표현할 수 없는 상황에 처할 수 있습니다.예를 들어 실패한 요청 재시도, 토큰을 사용한 재허가 흐름 또는 단계별 온보딩은 이렇게 기술하면 너무 상세하고 오류가 발생하기 쉽습니다.이 경우 Redux Saga 또는 Redux Loop과 같은 고급 비동기 제어 흐름 솔루션을 검토할 수 있습니다.그것들을 평가하고, 당신의 요구와 관련된 예를 비교하고, 당신이 가장 좋아하는 것을 고르세요.

마지막으로, 진정으로 필요한 것이 없다면 아무것도 사용하지 마십시오.요건에 따라서는 솔루션이 다음과 같이 단순해 보일 수 있습니다.

store.dispatch({ type: 'SHOW_NOTIFICATION', text: 'You logged in.' })
setTimeout(() => {
  store.dispatch({ type: 'HIDE_NOTIFICATION' })
}, 5000)

왜 이런 짓을 하는지 모르는 이상 걱정하지 마세요.

Redux-saga 사용

Dan Abramov가 말했듯이 비동기 코드에 대한 고급 제어가 필요한 경우 redux-saga를 살펴보십시오.

이 답변은 간단한 예시로, redux-saga가 어플리케이션에 도움이 되는 이유에 대해 더 자세히 설명하고 싶다면 이 다른 답변을 확인하십시오.

일반적으로 Redux-saga는 동기 코드처럼 보이는 비동기 코드를 쉽게 쓸 수 있는 ES6 제너레이터 인터프리터를 제공한다는 것입니다(이 때문에 Redux-saga에서는 루프를 반복하는 동안 무한대를 발견할 수 있습니다).어찌된 일인지 Redux-saga는 Javascript에 직접 언어를 구축하고 있다.레독스가의 경우 발전기에 대한 기본적인 이해가 필요하기 때문에 처음에는 조금 배우기 어려울 수 있지만 레독스가의 언어도 이해할 수긍할 수 있습니다.

여기에서는 레플렉스가 위에 제가 구축한 알림 시스템을 설명하겠습니다.이 예는 현재 실가동 중입니다.

고급 알림 시스템 사양

  • 알림 표시를 요청할 수 있습니다.
  • 알림을 요청하여 숨길 수 있습니다.
  • 알림은 4초 이상 표시되지 않아야 합니다.
  • 여러 알림을 동시에 표시할 수 있습니다.
  • 동시에 표시할 수 있는 알림은 최대 3개입니다.
  • 이미 3개의 알림이 표시되어 있을 때 알림이 요구되면 큐잉/포스트합니다.

결과

프로덕션 앱 Stample.co 스크린샷

토스트

코드

에서는, 에 「아, 아, 아, 아, 아」라고 이름을 .toast이건 이름 짓기 세부사항이에요

function* toastSaga() {

    // Some config constants
    const MaxToasts = 3;
    const ToastDisplayTime = 4000;
    

    // Local generator state: you can put this state in Redux store
    // if it's really important to you, in my case it's not really
    let pendingToasts = []; // A queue of toasts waiting to be displayed
    let activeToasts = []; // Toasts currently displayed


    // Trigger the display of a toast for 4 seconds
    function* displayToast(toast) {
        if ( activeToasts.length >= MaxToasts ) {
            throw new Error("can't display more than " + MaxToasts + " at the same time");
        }
        activeToasts = [...activeToasts,toast]; // Add to active toasts
        yield put(events.toastDisplayed(toast)); // Display the toast (put means dispatch)
        yield call(delay,ToastDisplayTime); // Wait 4 seconds
        yield put(events.toastHidden(toast)); // Hide the toast
        activeToasts = _.without(activeToasts,toast); // Remove from active toasts
    }

    // Everytime we receive a toast display request, we put that request in the queue
    function* toastRequestsWatcher() {
        while ( true ) {
            // Take means the saga will block until TOAST_DISPLAY_REQUESTED action is dispatched
            const event = yield take(Names.TOAST_DISPLAY_REQUESTED);
            const newToast = event.data.toastData;
            pendingToasts = [...pendingToasts,newToast];
        }
    }


    // We try to read the queued toasts periodically and display a toast if it's a good time to do so...
    function* toastScheduler() {
        while ( true ) {
            const canDisplayToast = activeToasts.length < MaxToasts && pendingToasts.length > 0;
            if ( canDisplayToast ) {
                // We display the first pending toast of the queue
                const [firstToast,...remainingToasts] = pendingToasts;
                pendingToasts = remainingToasts;
                // Fork means we are creating a subprocess that will handle the display of a single toast
                yield fork(displayToast,firstToast);
                // Add little delay so that 2 concurrent toast requests aren't display at the same time
                yield call(delay,300);
            }
            else {
                yield call(delay,50);
            }
        }
    }

    // This toast saga is a composition of 2 smaller "sub-sagas" (we could also have used fork/spawn effects here, the difference is quite subtile: it depends if you want toastSaga to block)
    yield [
        call(toastRequestsWatcher),
        call(toastScheduler)
    ]
}

그리고 환원제:

const reducer = (state = [],event) => {
    switch (event.name) {
        case Names.TOAST_DISPLAYED:
            return [...state,event.data.toastData];
        case Names.TOAST_HIDDEN:
            return _.without(state,event.data.toastData);
        default:
            return state;
    }
};

사용.

파견하면 요.TOAST_DISPLAY_REQUESTED4개를 알림이 사라지면 .4개의 요청을 디스패치하면 3개의 알림만 표시되며, 첫 번째 알림이 사라지면 4번째 알림이 조금 후에 표시됩니다.

하는 것은하지 않습니다.TOAST_DISPLAY_REQUESTEDJSX. 하고, 그후 존 the the the the add js js른 the the the the the the the the the the the the를 디스패치하는 것이 .TOAST_DISPLAY_REQUESTED: 알림을 트리거하는 컴포넌트는 알림 시스템에 긴밀하게 결합할 필요가 없습니다.

결론

내 코드는 완벽하지는 않지만 몇 달 동안 버그가 0개 있는 상태로 가동되고 있습니다.레독스가나 발전기는 처음에는 조금 어렵지만, 일단 이해하면 이 시스템을 구축하기 쉽습니다.

다음과 같은 복잡한 규칙을 구현하는 것도 매우 쉽습니다.

  • 너무 많은 통지가 "실행"되면 각 통지에 대한 표시 시간을 줄여 큐 크기를 더 빨리 줄일 수 있습니다.
  • 창 크기 변경을 검출하고 이에 따라 표시되는 알림의 최대 수를 변경합니다(예: desktop=3, 전화 세로 =2, 전화 가로 = 1).

솔직히, 이런 것들을 제대로 구현해내길 바라.

레독스가와 매우 유사한 레독스 관측 가능에도 동일한 작업을 수행할 수 있습니다.거의 비슷하고 발전기와 RxJ의 취향 문제입니다.

샘플 프로젝트가 있는 저장소

현재 4가지 샘플 프로젝트가 있습니다.

  1. 비동기 코드 인라인 쓰기
  2. 비동기 작업 생성기 추출
  3. Redx Thunk 사용
  4. Redux Saga 사용

인정된 답변은 훌륭합니다.

하지만 뭔가 부족한 게 있어요.

  1. 실행 가능한 샘플 프로젝트는 없고 코드 조각만 있습니다.
  2. 다음과 같은 다른 대안에는 샘플 코드가 없습니다.
    1. 레독스 사가

그래서 Hello Async 저장소를 생성하여 누락된 항목을 추가했습니다.

  1. 실행 가능한 프로젝트수정 없이 다운로드하여 실행할 수 있습니다.
  2. " " " " " " " " " 」 。

레독스 사가

승인된 답변은 이미 비동기 코드 인라인, 비동기 작업 생성기 및 Redux Thunk에 대한 샘플 코드 스니펫을 제공합니다.완전성을 위해 Redux Saga용 코드 스니펫을 제공합니다.

// actions.js

export const showNotification = (id, text) => {
  return { type: 'SHOW_NOTIFICATION', id, text }
}

export const hideNotification = (id) => {
  return { type: 'HIDE_NOTIFICATION', id }
}

export const showNotificationWithTimeout = (text) => {
  return { type: 'SHOW_NOTIFICATION_WITH_TIMEOUT', text }
}

행동은 단순하고 순수하다.

// component.js

import { connect } from 'react-redux'

// ...

this.props.showNotificationWithTimeout('You just logged in.')

// ...

export default connect(
  mapStateToProps,
  { showNotificationWithTimeout }
)(MyComponent)

컴포넌트에는 특별한 것이 없습니다.

// sagas.js

import { takeEvery, delay } from 'redux-saga'
import { put } from 'redux-saga/effects'
import { showNotification, hideNotification } from './actions'

// Worker saga
let nextNotificationId = 0
function* showNotificationWithTimeout (action) {
  const id = nextNotificationId++
  yield put(showNotification(id, action.text))
  yield delay(5000)
  yield put(hideNotification(id))
}

// Watcher saga, will invoke worker saga above upon action 'SHOW_NOTIFICATION_WITH_TIMEOUT'
function* notificationSaga () {
  yield takeEvery('SHOW_NOTIFICATION_WITH_TIMEOUT', showNotificationWithTimeout)
}

export default notificationSaga

Saga는 ES6 제너레이터를 기반으로 합니다.

// index.js

import createSagaMiddleware from 'redux-saga'
import saga from './sagas'

const sagaMiddleware = createSagaMiddleware()

const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)

sagaMiddleware.run(saga)

Redx Thunk와 비교

장점

  • 콜백 지옥으로 갈 순 없어
  • 비동기 플로우를 쉽게 테스트할 수 있습니다.
  • 당신의 행동은 순수하게 유지된다.

단점

  • 비교적 새로운 ES6 발전기에 의존합니다.

위의 코드 스니펫이 모든 질문에 답하지 않을 경우 실행 가능한 프로젝트를 참조하십시오.

redux-thunk로 할 수 있습니다.redux 문서에는 set Timeout 등의 비동기 액션에 대한 가이드가 있습니다.

SAM 패턴도 확인해 보시기 바랍니다.

SAM 패턴은 모델이 갱신되면 (SAM 모델~리듀서 상태+스토어) "알림 자동 소실"과 같은 (자동) 액션이 트리거되는 "next-action-predicate" 포함을 지지합니다.

패턴은 모델의 "제어 상태"가 다음 동작 술어에 의해 활성화되거나 자동으로 실행되는 동작을 "제어"하기 때문에 동작과 모델 돌연변이를 한 번에 하나씩 정렬하는 것을 지지합니다.액션을 처리하기 전에 시스템이 어떤 상태인지(일반적으로) 예측할 수 없기 때문에 다음에 예상되는 액션이 허용되는지 또는 가능한지 여부를 예측할 수 없습니다.

그래서 예를 들면, 코드,

export function showNotificationWithTimeout(dispatch, text) {
  const id = nextNotificationId++
  dispatch(showNotification(id, text))

  setTimeout(() => {
    dispatch(hideNotification(id))
  }, 5000)
}

hideNotification 액션을 디스패치할 수 있는지 여부는 모델에 따라 "showNotification: true" 값이 정상적으로 받아들여지는지에 따라 다르기 때문에 SAM에서는 허용되지 않습니다.모델에는 다른 부분이 있을 수 있으므로 hideNotification 액션을 트리거할 필요가 없습니다.

스토어 업데이트 후 적절한 다음 조치 술어를 구현하여 모델의 새로운 제어 상태를 알 수 있도록 적극 권장합니다.그것이 당신이 찾고 있는 행동을 실행하는 가장 안전한 방법입니다.

괜찮으시다면 기터로 합류하셔도 됩니다여기에는 SAM 시작 가이드도 있습니다.

다양한 인기 어프로치(액션 크리에이터, thunks, sagas, epic, effects, 커스텀 미들웨어)를 사용해 본 결과, 개선의 여지가 있다고 느꼈기 때문에, 이 블로그 기사 「React/Redux 애플리케이션의 비즈니스 로직을 어디에 둘 것인가」에서 제 여정을 기록했습니다.

여기서의 논의와 마찬가지로, 저는 다양한 접근 방식을 대조하고 비교하려고 했습니다.그 결과, Epic, Sagas, 커스텀 미들웨어에서 영감을 얻은 새로운 라이브러리 redux-logic을 도입하게 되었습니다.

이를 통해 검증, 검증, 인가 작업을 대행 수신할 수 있을 뿐만 아니라 비동기 IO를 수행하는 방법을 제공할 수 있습니다.

디버깅, 슬롯링, 취소, 최신 요청 응답(takeLatest)만을 사용하여 선언할 수 있는 일반적인 기능도 있습니다.redux-logic은 이 기능을 제공하는 코드를 래핑합니다.

따라서 핵심 비즈니스 로직을 원하는 대로 구현할 수 있습니다.원하지 않는 한 관측 가능 또는 생성기를 사용할 필요가 없습니다.함수 및 콜백, 약속, 비동기 함수(async/ait) 등을 사용합니다.

간단한 5s 알림을 수행하기 위한 코드는 다음과 같습니다.

const notificationHide = createLogic({
  // the action type that will trigger this logic
  type: 'NOTIFICATION_DISPLAY',
  
  // your business logic can be applied in several
  // execution hooks: validate, transform, process
  // We are defining our code in the process hook below
  // so it runs after the action hit reducers, hide 5s later
  process({ getState, action }, dispatch) {
    setTimeout(() => {
      dispatch({ type: 'NOTIFICATION_CLEAR' });
    }, 5000);
  }
});
    

Sebastian Lorber가 설명한 것과 유사한 고급 알림 예가 내 repo에 있습니다.여기서 표시를 N개의 항목으로 제한하고 큐잉된 항목을 순환할 수 있습니다.redux-logic 알림

다양한 redux-logic jsfiddle 라이브 예시와 전체 예시를 보유하고 있습니다.저는 문서와 예제를 계속 연구하고 있습니다.

당신의 의견을 듣고 싶습니다.

이 질문이 다소 오래된 것은 이해하지만, 저는 redux-observatible aka를 사용하여 다른 솔루션을 소개하겠습니다.서사시.

공식 문서 인용:

redex-observatible은 무엇입니까?

RxJs 5 기반 미들웨어(Redux용).비동기 작업을 구성 및 취소하여 부작용 등을 생성합니다.

서사시는 레독스 관찰 가능성의 핵심 원초적인 것이다.

동작의 스트림을 취하고 동작의 스트림을 반환하는 함수입니다.액션 인, 액션 아웃.

즉, Stream을 통해 액션을 수신하는 함수를 만든 다음 새로운 액션스트림을 반환할 수 있습니다(타임아웃, 지연, 인터벌, 요청 등 일반적인 부작용 사용).

코드를 올리고 좀 더 자세히 설명하겠습니다.

store.displaces를 설정합니다.

import {createStore, applyMiddleware} from 'redux'
import {createEpicMiddleware} from 'redux-observable'
import {Observable} from 'rxjs'
const NEW_NOTIFICATION = 'NEW_NOTIFICATION'
const QUIT_NOTIFICATION = 'QUIT_NOTIFICATION'
const NOTIFICATION_TIMEOUT = 2000

const initialState = ''
const rootReducer = (state = initialState, action) => {
  const {type, message} = action
  console.log(type)
  switch(type) {
    case NEW_NOTIFICATION:
      return message
    break
    case QUIT_NOTIFICATION:
      return initialState
    break
  }

  return state
}

const rootEpic = (action$) => {
  const incoming = action$.ofType(NEW_NOTIFICATION)
  const outgoing = incoming.switchMap((action) => {
    return Observable.of(quitNotification())
      .delay(NOTIFICATION_TIMEOUT)
      //.takeUntil(action$.ofType(NEW_NOTIFICATION))
  });

  return outgoing;
}

export function newNotification(message) {
  return ({type: NEW_NOTIFICATION, message})
}
export function quitNotification(message) {
  return ({type: QUIT_NOTIFICATION, message});
}

export const configureStore = () => createStore(
  rootReducer,
  applyMiddleware(createEpicMiddleware(rootEpic))
)

index.displaces를 표시합니다.

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {configureStore} from './store.js'
import {Provider} from 'react-redux'

const store = configureStore()

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

App.js

import React, { Component } from 'react';
import {connect} from 'react-redux'
import {newNotification} from './store.js'

class App extends Component {

  render() {
    return (
      <div className="App">
        {this.props.notificationExistance ? (<p>{this.props.notificationMessage}</p>) : ''}
        <button onClick={this.props.onNotificationRequest}>Click!</button>
      </div>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    notificationExistance : state.length > 0,
    notificationMessage : state
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onNotificationRequest: () => dispatch(newNotification(new Date().toDateString()))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

이 문제를 해결하기 위한 키코드는 보시다시피 간단합니다.다른 답변과 다른 것은 rootEpic 함수뿐입니다.

포인트 1sagas와 마찬가지로 동작 스트림을 수신하고 동작 스트림을 반환하는 최상위 함수를 얻으려면 에픽스를 조합해야 합니다.따라서 미들웨어 팩토리 createEpicMiddleware에서 사용할 수 있습니다.이 경우 1개만 필요하기 때문에 rootEpic밖에 없기 때문에 어떤 것도 조합할 필요는 없지만 사실을 아는 것이 좋습니다.

포인트 2부작용 로직을 배려한 rootEpic은 코드 5줄 정도밖에 필요 없어 대박!거의 선언적인 사실까지 포함해서!

포인트 3한 줄 한 줄 rootEpic 설명(댓글)

const rootEpic = (action$) => {
  // sets the incoming constant as a stream 
  // of actions with  type NEW_NOTIFICATION
  const incoming = action$.ofType(NEW_NOTIFICATION)
  // Merges the "incoming" stream with the stream resulting for each call
  // This functionality is similar to flatMap (or Promise.all in some way)
  // It creates a new stream with the values of incoming and 
  // the resulting values of the stream generated by the function passed
  // but it stops the merge when incoming gets a new value SO!,
  // in result: no quitNotification action is set in the resulting stream
  // in case there is a new alert
  const outgoing = incoming.switchMap((action) => {
    // creates of observable with the value passed 
    // (a stream with only one node)
    return Observable.of(quitNotification())
      // it waits before sending the nodes 
      // from the Observable.of(...) statement
      .delay(NOTIFICATION_TIMEOUT)
  });
  // we return the resulting stream
  return outgoing;
}

도움이 됐으면 좋겠네요!

왜 그렇게 힘들까?UI 논리일 뿐입니다.통지 데이터를 설정하려면 전용 작업을 사용합니다.

dispatch({ notificationData: { message: 'message', expire: +new Date() + 5*1000 } })

및 이를 표시하는 전용 컴포넌트:

const Notifications = ({ notificationData }) => {
    if(notificationData.expire > this.state.currentTime) {
      return <div>{notificationData.message}</div>
    } else return null;
}

이 경우 질문은 "구 상태를 어떻게 정리합니까?", "구성 요소에 시간이 변경되었음을 알리는 방법"이어야 합니다.

컴포넌트에서 setTimeout에 디스패치되는 몇 가지 타임아웃 액션을 구현할 수 있습니다.

새 알림이 표시될 때마다 청소하는 것이 좋을 수도 있습니다.

'아까', '아까', '아까', '아까'가예요.setTimeout디선,, 맞??

setTimeout(() => this.setState({ currentTime: +new Date()}), 
           this.props.notificationData.expire-(+new Date()) )

그 이유는 "알림 페이드 아웃" 기능이 UI에 정말로 문제가 되기 때문입니다.따라서 비즈니스 로직 테스트를 간소화할 수 있습니다.

어떻게 구현되는지 테스트하는 것은 말이 되지 않는 것 같습니다.통지가 타임아웃 되는 타이밍을 확인하는 것만이 의미가 있습니다.따라서 stub 코드 감소, 테스트 속도 향상, 코드 청소가 가능합니다.

선택적 액션에 대한 시간 초과 처리를 원하는 경우 미들웨어 접근 방식을 사용할 수 있습니다.약속에 기초한 행동을 선별적으로 처리하는 것과 유사한 문제에 직면했고, 이 솔루션은 더 유연했습니다.

액션 크리에이터는 다음과 같습니다.

//action creator
buildAction = (actionData) => ({
    ...actionData,
    timeout: 500
})

타임아웃은 위의 액션에서 여러 값을 유지할 수 있습니다.

  • number(ms) - 특정 타임아웃 기간
  • true - 일정한 타임아웃 기간 동안.(미들웨어에 삽입)
  • 미정의 - 즉시 디스패치용

미들웨어의 실장은 다음과 같습니다.

//timeoutMiddleware.js
const timeoutMiddleware = store => next => action => {

  //If your action doesn't have any timeout attribute, fallback to the default handler
  if(!action.timeout) {
    return next (action)
  }

  const defaultTimeoutDuration = 1000;
  const timeoutDuration = Number.isInteger(action.timeout) ? action.timeout || defaultTimeoutDuration;

//timeout here is called based on the duration defined in the action.
  setTimeout(() => {
    next (action)
  }, timeoutDuration)
}

이제 redux를 사용하여 이 미들웨어 계층을 통해 모든 작업을 라우팅할 수 있습니다.

createStore(reducer, applyMiddleware(timeoutMiddleware))

유사한 예를 여기에서 찾을 수 있습니다.

이를 위한 적절한 방법은 Redx Thunk 설명서에 따라 Redx에서 널리 사용되는 미들웨어인 Redx Thunk를 사용하는 것입니다.

Redux Thunk 미들웨어는 액션 대신 함수를 반환하는 액션 크리에이터를 작성할 수 있습니다.트렁크를 사용하여 액션의 디스패치를 지연시키거나 특정 조건이 충족된 경우에만 디스패치를 수행할 수 있습니다.내부 함수는 store methods dispatch 및 getState를 파라미터로 수신합니다."

따라서 기본적으로 기능이 반환되며 디스패치를 지연시키거나 상태 그대로 둘 수 있습니다.

다음과 같은 방법으로 작업을 수행할 수 있습니다.

import ReduxThunk from 'redux-thunk';

const INCREMENT_COUNTER = 'INCREMENT_COUNTER';

function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}

function incrementAsync() {
  return dispatch => {
    setTimeout(() => {
      // Yay! Can invoke sync or async actions with `dispatch`
      dispatch(increment());
    }, 5000);
  };
}

Redx 자체는 매우 상세한 라이브러리입니다.그러기 위해서는 Redx-thunk 같은 것을 사용해야 합니다.이것에 의해, Redux-thunk는, 다음의 정보를 얻을 수 있습니다.dispatch몇 초 후에 알림 마감을 디스패치할 수 있습니다.

장황함이나 구성성 등의 문제에 대처하기 위해 라이브러리를 작성했습니다.이 예는 다음과 같습니다.

import { createTile, createSyncTile } from 'redux-tiles';
import { sleep } from 'delounce';

const notifications = createSyncTile({
  type: ['ui', 'notifications'],
  fn: ({ params }) => params.data,
  // to have only one tile for all notifications
  nesting: ({ type }) => [type],
});

const notificationsManager = createTile({
  type: ['ui', 'notificationManager'],
  fn: ({ params, dispatch, actions }) => {
    dispatch(actions.ui.notifications({ type: params.type, data: params.data }));
    await sleep(params.timeout || 5000);
    dispatch(actions.ui.notifications({ type: params.type, data: null }));
    return { closed: true };
  },
  nesting: ({ type }) => [type],
});

따라서 비동기 액션 내에 알림을 표시하기 위한 동기 액션을 구성합니다.이것에 의해, 백그라운드의 정보를 요구하거나, 통지가 수동으로 닫혔는지를 나중에 확인할 수 있습니다.

그것은 간단하다.trim-redux 패키지를 사용하여 다음과 같이 적습니다.componentDidMountcomponentWillUnmount.

componentDidMount() {
  this.tm = setTimeout(function() {
    setStore({ age: 20 });
  }, 3000);
}

componentWillUnmount() {
  clearTimeout(this.tm);
}

이것은 다소 주제에서 벗어난 내용일 수 있지만, 저는 단순히 특정 시간 초과 후(즉, 경보/알림 자동 숨기기) 경보를 상태로부터 삭제하고 싶었기 때문에 여기서 공유하려고 합니다.

나는 결국 그것을 이용하게 되었다.setTimeout()의 범위 내에서<Alert />컴포넌트를할 수 .REMOVEid.

export function Alert(props: Props) {
  useEffect(() => {
    const timeoutID = setTimeout(() => {
      dispatchAction({
        type: REMOVE,
        payload: {
          id: id,
        },
      });
    }, timeout ?? 2000);
    return () => clearTimeout(timeoutID);
  }, []);
  return <AlertComponent {...props} />;
}

Redux 액션은 함수, 콜백 또는 비동기 프로세스가 아닌 플레인 객체만 반환할 수 있습니다.timeout() 메서드 등의 웹 API를 통해 디스패치하려면 redux-thunk 미들웨어를 사용해야 합니다.이러한 프로세스를 처리하기 위해 작성되었습니다.

  1. documentation redux-thunk를 통한 첫 번째 구성 redux-thunk
  2. 두 번째로 작업 작성자를 다음과 같이 변경합니다.
const yourAction = millisecond => dispatch => {
   setTimeout(() => {
      dispatch({
         type: 'YOUR_ACTIION_TYPE',
         payload: yourWhatEverPayload
      })
   }, millisecond)
}

언급URL : https://stackoverflow.com/questions/35411423/how-to-dispatch-a-redux-action-with-a-timeout