도서관이 모든 것을하는 방법을 처방해야한다고 생각하는 함정에 빠지지 마십시오 . JavaScript에서 시간 초과가있는 작업을 수행하려면을 사용해야 setTimeout
합니다. Redux 작업이 다른 이유는 없습니다.
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)
또는 이전에 다음을 바인딩 한 경우 connect()
:
this.props.showNotification('You just logged in.')
setTimeout(() => {
this.props.hideNotification()
}, 5000)
지금까지 미들웨어 또는 기타 고급 개념을 사용하지 않았습니다.
비동기 액션 생성기 추출
위의 방법은 간단한 경우에는 잘 작동하지만 몇 가지 문제가 있음을 알 수 있습니다.
- 알림을 표시하려는 모든 위치에서이 논리를 복제해야합니다.
- 알림에는 ID가 없으므로 두 개의 알림을 충분히 빨리 표시하면 경쟁 조건이 발생합니다. 첫 번째 시간 초과가 끝나면을 전달
HIDE_NOTIFICATION
하여 시간 초과 후보다 빨리 두 번째 알림을 숨 깁니다.
이러한 문제점을 해결하려면 시간 종료 로직을 중앙 집중화하고 해당 두 조치를 전달하는 함수를 추출해야합니다. 다음과 같이 보일 수 있습니다 :
// 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
있지만 외부 함수가 디스패치를 제어하기를 원하므로 디스패치에 대한 제어를 제공해야합니다.
일부 모듈에서 단일 저장소를 내 보낸 경우 가져 와서 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.')
이 방법은 더 단순 해 보이지만 이 방법은 권장하지 않습니다 . 우리가 싫어하는 주된 이유는 store를 singleton으로 강제 하기 때문 입니다. 따라서 서버 렌더링 을 구현하기가 매우 어렵습니다 . 서버에서 각 요청마다 고유 한 저장소를 가지도록하여 다른 사용자가 서로 다른 사전로드 된 데이터를 얻도록합니다.
단일 저장소도 테스트를 어렵게 만듭니다. 조치 작성자가 특정 모듈에서 내 보낸 특정 실제 상점을 참조하므로 조치 작성자를 테스트 할 때 더 이상 상점을 조롱 할 수 없습니다. 외부에서 상태를 재설정 할 수도 없습니다.
따라서 기술적으로 모듈에서 단일 저장소를 내보낼 수는 있지만 권장하지는 않습니다. 앱에서 서버 렌더링을 추가하지 않을 것이 아니라면이 작업을 수행하지 마십시오.
이전 버전으로 돌아 가기 :
// 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
. 위와 같은 방식으로 Redux 작업을 비동기식으로 전달하는 모든 구성 요소 는 소품 으로 받아 들여 더 이상 전달할 수 있기 때문에 컨테이너와 프레젠테이션 구성 요소 를 분리 하기가 더 까다로워 dispatch
집니다. 실제로 액션 제작자가 아니기 connect()
때문에 액션 제작자를 더 이상 바인딩 할 수 없습니다 showNotificationWithTimeout()
. Redux 작업을 반환하지 않습니다.
또한 어떤 함수가 동기 작업 작성자와 같은 showNotification()
비동기 도우미 인지 기억하는 것은 어색 할 수 있습니다 showNotificationWithTimeout()
. 서로 다르게 사용해야하며 서로 실수하지 않도록주의해야합니다.
이것은 도우미 기능에 이러한 패턴을 제공하는 방법을 찾고, dispatch
Redux가 이러한 비동기식 액션 제작자를 완전히 다른 기능이 아닌 일반 액션 제작자의 특별한 경우로 "볼 수 있도록 " 하는 동기를 부여했습니다 .
여전히 우리와 함께 있고 앱에서 문제로 인식되면 Redux Thunk 미들웨어 를 사용하는 것이 좋습니다 .
요점에서 Redux Thunk는 Redux에게 실제로 작동하는 특수한 종류의 작업을 인식하도록 지시합니다.
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)
})
이 미들웨어가 활성화 된 상태에서 함수를 디스패치하면 Redux Thunk 미들웨어가이를 dispatch
인수로 제공합니다 . 또한 그러한 동작을 "삼키기"때문에 감속기가 이상한 함수 인수를받는 것에 대해 걱정하지 마십시오. 감속기는 직접 방출되거나 방금 설명한 기능에 의해 방출되는 일반 물체 작동 만받습니다.
이것은 매우 유용하게 보이지 않습니까? 이 특별한 상황에는 없습니다. 그러나 showNotificationWithTimeout()
일반적인 Redux 액션 제작자로 선언 할 수 있습니다 .
// 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
.
그러나 이것은 원래 버전보다 훨씬 어색합니다! 우리는 왜 그런 식으로 갔습니까?
내가 전에 한 말 때문에 Redux Thunk 미들웨어가 사용 가능한 경우 조치 오브젝트 대신 함수를 디스패치하려고 시도 할 때마다 미들웨어는 dispatch
메소드 자체를 첫 번째 인수로 사용하여 해당 함수를 호출합니다 .
그래서 우리는 대신 이것을 할 수 있습니다 :
// component.js
this.props.dispatch(showNotificationWithTimeout('You just logged in.'))
마지막으로, 비동기 액션 (실제로 일련의 액션)을 디스패치하는 것은 단일 액션을 컴포넌트에 동 기적으로 디스패치하는 것과 다르지 않습니다. 구성 요소가 동 기적으로 또는 비동기 적으로 발생하는지 여부를 신경 쓰지 않아야하기 때문에 좋습니다. 우리는 그것을 추상화했습니다.
우리가 "가르쳐"이후 돌아 오는이 (우리가 그들에게 전화 등의 "특별한"액션 제작자를 인식하는 것을 알 수 썽크 우리는 일반 액션 제작자을 사용 곳 액션 제작자), 우리는 지금 어떤 장소에서 사용할 수 있습니다. 예를 들어 다음과 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)
뭉크의 읽기 상태
일반적으로 감속기는 다음 상태를 결정하기위한 비즈니스 로직을 포함합니다. 그러나 감속기는 작업이 발송 된 후에 만 시작됩니다. 썽크 액션 생성자에 부작용 (예 : API 호출)이 있고 어떤 조건에서이를 방지하려면 어떻게해야합니까?
썽크 미들웨어를 사용하지 않고 구성 요소 내부 에서이 검사를 수행하면됩니다.
// component.js
if (this.props.areNotificationsEnabled) {
showNotificationWithTimeout(this.props.dispatch, 'You just logged in.')
}
그러나 액션 제작자를 추출하는 요점은 여러 구성 요소에서이 반복적 인 논리를 중앙 집중화하는 것이 었습니다. 다행히 Redux Thunk는 Redux 저장소의 현재 상태 를 읽을 수있는 방법을 제공합니다 . 뿐만 아니라 썽크 액션 생성자에서 반환하는 함수에 대한 두 번째 인수로 dispatch
도 전달 getState
됩니다. 이를 통해 썽 크는 상점의 현재 상태를 읽을 수 있습니다.
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()
조건부로 다른 조치를 디스패치 하는 데만 사용 하는 경우 비즈니스 로직을 리듀서에 대신 사용하십시오.
다음 단계
썽크의 작동 방식에 대한 기본적인 이해가 있으므로 이를 사용하는 Redux 비동기 예제 를 확인하십시오 .
썽크가 약속을 반환하는 많은 예를 찾을 수 있습니다. 이것은 필수는 아니지만 매우 편리합니다. Redux는 썽크에서 반환하는 것을 신경 쓰지 않지만에서 반환 값을 제공합니다 dispatch()
. 그렇기 때문에 썽크에서 약속을 반환하고을 호출하여 완료 될 때까지 기다릴 수 있습니다 dispatch(someThunkReturningPromise()).then(...)
.
복잡한 썽크 액션 제작자를 여러 개의 작은 썽크 액션 제작자로 나눌 수도 있습니다. dispatch
재귀 패턴을 적용 할 수 있도록 썽크에 의해 제공 방법은, 그 자체 썽크 받아 들일 수 있습니다. 다시 말하지만, 비동기 제어 흐름을 구현할 수 있기 때문에 Promises와 가장 잘 작동합니다.
일부 앱의 경우 비동기 제어 흐름 요구 사항이 너무 복잡하여 썽 크로 표현할 수없는 상황에 처할 수 있습니다. 예를 들어, 실패한 요청을 재 시도하거나 토큰을 사용한 재 인증 흐름 또는 단계별 온 보딩은 너무 자세하고 오류가 발생하기 쉽습니다. 이 경우 Redux Saga 또는 Redux Loop 와 같은 고급 비동기 제어 흐름 솔루션을 살펴볼 수 있습니다 . 그것들을 평가하고, 귀하의 필요와 관련된 예를 비교하고, 가장 좋아하는 것을 선택하십시오.
마지막으로 꼭 필요한 것이 없다면 썽크를 포함하여 아무 것도 사용하지 마십시오. 요구 사항에 따라 솔루션은 다음과 같이 단순 해 보일 수 있습니다.
store.dispatch({ type: 'SHOW_NOTIFICATION', text: 'You logged in.' })
setTimeout(() => {
store.dispatch({ type: 'HIDE_NOTIFICATION' })
}, 5000)
왜 이런 짓을하는지 모른다면 땀을 흘리지 마십시오.
redux-saga
썽크보다 더 나은 것을 원한다면 내 대답 을 확인하는 것을 잊지 마십시오 . 늦은 답변이므로 표시하기 전에 오래 스크롤해야합니다. :) 읽을 가치가 없다는 것을 의미하지는 않습니다. 지름길은 다음과 같습니다. stackoverflow.com/a/38574266/82609