Làm thế nào để gửi một hành động Redux với thời gian chờ?


891

Tôi có một hành động cập nhật trạng thái thông báo của ứng dụng của tôi. Thông thường, thông báo này sẽ là một lỗi hoặc thông tin của một số loại. Sau đó, tôi cần gửi một hành động khác sau 5 giây sẽ trả lại trạng thái thông báo về trạng thái ban đầu, do đó không có thông báo. Lý do chính đằng sau điều này là để cung cấp chức năng trong đó các thông báo tự động biến mất sau 5 giây.

Tôi không có may mắn với việc sử dụng setTimeoutvà trả lại một hành động khác và không thể tìm thấy cách thức này được thực hiện trực tuyến. Vì vậy, bất kỳ lời khuyên đều được chào đón.


30
Đừng quên kiểm tra redux-sagacâu trả lời của tôi nếu bạn muốn thứ gì đó tốt hơn thunks. Câu trả lời muộn để bạn phải cuộn một lúc lâu trước khi thấy nó xuất hiện :) không có nghĩa là nó không đáng đọc. Đây là một phím tắt: stackoverflow.com/a/38574266/82609
Sebastien Lorber

5
Bất cứ khi nào bạn thực hiện setTimeout, đừng quên xóa bộ đếm thời gian bằng cách sử dụng ClearTimeout trong phương thức vòng đời của thành
phầnWillUnMount

2
redux-saga rất tuyệt nhưng dường như họ không hỗ trợ cho các phản hồi đánh máy từ các chức năng của trình tạo. Có thể có vấn đề nếu bạn đang sử dụng bản thảo với phản ứng.
Crhistian Ramirez

Câu trả lời:


2617

Đừng rơi vào cái bẫy nghĩ rằng một thư viện nên quy định cách làm mọi thứ . Nếu bạn muốn làm một cái gì đó với thời gian chờ trong JavaScript, bạn cần sử dụng setTimeout. Không có lý do tại sao các hành động Redux nên khác nhau.

Redux không cung cấp một số cách khác nhau để đối phó với những thứ không đồng bộ, nhưng bạn chỉ nên sử dụng những khi bạn nhận ra bạn đang lặp đi lặp lại quá nhiều mã. Trừ khi bạn gặp vấn đề này, hãy sử dụng những gì ngôn ngữ cung cấp và đưa ra giải pháp đơn giản nhất.

Viết nội tuyến mã Async

Đây là cách đơn giản nhất. Và không có gì cụ thể cho Redux ở đây.

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

Tương tự, từ bên trong một thành phần được kết nối:

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

Sự khác biệt duy nhất là trong một thành phần được kết nối, bạn thường không có quyền truy cập vào cửa hàng, nhưng có được dispatch()hoặc người tạo hành động cụ thể được tiêm làm đạo cụ. Tuy nhiên điều này không làm cho bất kỳ sự khác biệt cho chúng tôi.

Nếu bạn không thích mắc lỗi chính tả khi gửi cùng một hành động từ các thành phần khác nhau, bạn có thể muốn trích xuất các trình tạo hành động thay vì gửi các đối tượng hành động nội tuyến:

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

Hoặc, nếu trước đây bạn đã ràng buộc họ với connect():

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

Cho đến nay chúng tôi chưa sử dụng bất kỳ phần mềm trung gian hoặc khái niệm nâng cao nào khác.

Trích xuất trình tạo hành động Async

Cách tiếp cận trên hoạt động tốt trong các trường hợp đơn giản nhưng bạn có thể thấy rằng nó có một vài vấn đề:

  • Nó buộc bạn phải sao chép logic này bất cứ nơi nào bạn muốn hiển thị thông báo.
  • Các thông báo không có ID nên bạn sẽ có điều kiện cuộc đua nếu bạn hiển thị hai thông báo đủ nhanh. Khi hết thời gian chờ đầu tiên, nó sẽ gửi đi HIDE_NOTIFICATION, ẩn nhầm thông báo thứ hai sớm hơn sau khi hết thời gian.

Để giải quyết những vấn đề này, bạn sẽ cần trích xuất một hàm tập trung logic hết thời gian và gửi hai hành động đó. Nó có thể trông như thế này:

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

Bây giờ các thành phần có thể sử dụng showNotificationWithTimeoutmà không cần sao chép logic này hoặc có các điều kiện cuộc đua với các thông báo khác nhau:

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

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

Tại sao không showNotificationWithTimeout()chấp nhận dispatchlà đối số đầu tiên? Bởi vì nó cần phải gửi hành động đến cửa hàng. Thông thường một thành phần có quyền truy cập dispatchnhưng vì chúng ta muốn một chức năng bên ngoài kiểm soát việc gửi đi, chúng ta cần cho nó quyền kiểm soát việc gửi đi.

Nếu bạn có một cửa hàng đơn lẻ được xuất từ ​​một số mô-đun, bạn chỉ có thể nhập nó và dispatchtrực tiếp trên nó thay thế:

// 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.')    

Điều này có vẻ đơn giản hơn nhưng chúng tôi không khuyến nghị phương pháp này . Lý do chính khiến chúng tôi không thích nó là vì nó buộc cửa hàng phải là một người độc thân . Điều này làm cho nó rất khó để thực hiện kết xuất máy chủ . Trên máy chủ, bạn sẽ muốn mỗi yêu cầu có cửa hàng riêng để những người dùng khác nhau nhận được dữ liệu tải sẵn khác nhau.

Một cửa hàng đơn lẻ cũng làm cho thử nghiệm khó hơn. Bạn không còn có thể chế nhạo một cửa hàng khi thử nghiệm người tạo hành động vì họ tham chiếu một cửa hàng thực cụ thể được xuất từ ​​một mô-đun cụ thể. Bạn thậm chí không thể thiết lập lại trạng thái của nó từ bên ngoài.

Vì vậy, trong khi về mặt kỹ thuật bạn có thể xuất một cửa hàng đơn lẻ từ một mô-đun, chúng tôi không khuyến khích nó. Đừng làm điều này trừ khi bạn chắc chắn rằng ứng dụng của bạn sẽ không bao giờ thêm kết xuất máy chủ.

Quay trở lại phiên bản trước:

// 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.')    

Điều này giải quyết các vấn đề với sự trùng lặp logic và cứu chúng ta khỏi các điều kiện chủng tộc.

Middleware

Đối với các ứng dụng đơn giản, cách tiếp cận nên đủ. Đừng lo lắng về phần mềm trung gian nếu bạn hài lòng với nó.

Tuy nhiên, trong các ứng dụng lớn hơn, bạn có thể tìm thấy những bất tiện nhất định xung quanh nó.

Chẳng hạn, có vẻ đáng tiếc là chúng ta phải vượt qua dispatch. Điều này làm cho việc phân tách các thành phần chứa và trình bày trở nên khó khăn hơn bởi vì bất kỳ thành phần nào gửi các hành động Redux không đồng bộ theo cách trên đều phải chấp nhận dispatchnhư một chỗ dựa để nó có thể vượt qua nó. Bạn không thể liên kết người tạo hành động với connect()nữa vì showNotificationWithTimeout()thực sự không phải là người tạo hành động. Nó không trả về một hành động Redux.

Ngoài ra, có thể khó xử khi nhớ chức năng nào là người tạo hành động đồng bộ thích showNotification()và người trợ giúp không đồng bộ thích showNotificationWithTimeout(). Bạn phải sử dụng chúng khác nhau và cẩn thận để không nhầm lẫn chúng với nhau.

Đây là động lực để tìm cách hợp thức hóa các mô hình cung cấp dispatchchức năng của người trợ giúp và giúp Redux cảm thấy các nhà sáng tạo hành động không đồng bộ như một trường hợp đặc biệt của những người tạo hành động bình thường thay vì các chức năng hoàn toàn khác nhau.

Nếu bạn vẫn ở với chúng tôi và bạn cũng nhận ra đó là một vấn đề trong ứng dụng của mình, bạn có thể sử dụng phần mềm trung gian Redux Thunk .

Trong một ý chính, Redux Thunk dạy Redux nhận ra các loại hành động đặc biệt có chức năng thực tế:

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

Khi phần mềm trung gian này được bật, nếu bạn gửi một hàm , phần mềm trung gian Redux Thunk sẽ cung cấp cho nó dispatchlàm đối số. Nó cũng sẽ nuốt những hành động như vậy vì vậy đừng lo lắng về việc bộ giảm của bạn nhận được các đối số chức năng kỳ lạ. Bộ giảm tốc của bạn sẽ chỉ nhận được các hành động đối tượng đơn giản, được phát ra trực tiếp hoặc được phát ra bởi các chức năng như chúng tôi vừa mô tả.

Điều này có vẻ không hữu ích lắm phải không? Không phải trong tình huống đặc biệt này. Tuy nhiên, nó cho phép chúng tôi tuyên bố showNotificationWithTimeout()là người tạo hành động Redux thông thường:

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

Lưu ý cách hàm gần giống với hàm chúng ta đã viết trong phần trước. Tuy nhiên, nó không chấp nhận dispatchnhư là đối số đầu tiên. Thay vào đó, nó trả về một hàm chấp nhận dispatchlàm đối số đầu tiên.

Làm thế nào chúng ta sẽ sử dụng nó trong thành phần của chúng tôi? Chắc chắn, chúng ta có thể viết điều này:

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

Chúng tôi đang gọi trình tạo hành động async để có được chức năng bên trong muốn dispatch, và sau đó chúng tôi vượt qua dispatch.

Tuy nhiên điều này thậm chí còn khó xử hơn phiên bản gốc! Tại sao chúng ta thậm chí đi theo cách đó?

Bởi vì những gì tôi nói với bạn trước đây. Nếu Redux Thunk middleware được bật, bất cứ khi nào bạn cố gắng gửi một hàm thay vì một đối tượng hành động, phần mềm trung gian sẽ gọi hàm đó với dispatchchính phương thức đó là đối số đầu tiên .

Vì vậy, chúng ta có thể làm điều này thay vào đó:

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

Cuối cùng, việc gửi một hành động không đồng bộ (thực sự, một loạt các hành động) trông không khác gì gửi một hành động đồng bộ đến thành phần. Điều này là tốt bởi vì các thành phần không nên quan tâm liệu một cái gì đó xảy ra đồng bộ hay không đồng bộ. Chúng tôi chỉ trừu tượng hóa đi.

Lưu ý rằng vì chúng tôi đã dạy dạy Red Redux để nhận ra những người tạo hành động đặc biệt như thế này (chúng tôi gọi họ là người tạo hành động thunk ), giờ đây chúng tôi có thể sử dụng chúng ở bất kỳ nơi nào chúng tôi sẽ sử dụng người tạo hành động thông thường. Ví dụ: chúng ta có thể sử dụng chúng với 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)

Đọc trạng thái trong Thunks

Thông thường bộ giảm tốc của bạn chứa logic nghiệp vụ để xác định trạng thái tiếp theo. Tuy nhiên, bộ giảm tốc chỉ đá sau khi các hành động được gửi đi. Điều gì xảy ra nếu bạn có một tác dụng phụ (như gọi API) trong trình tạo hành động thunk và bạn muốn ngăn chặn nó trong một số điều kiện?

Không sử dụng phần mềm trung gian thunk, bạn chỉ cần thực hiện kiểm tra này bên trong thành phần:

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

Tuy nhiên, điểm trích xuất của một người tạo hành động là tập trung logic lặp đi lặp lại này trên nhiều thành phần. May mắn thay, Redux Thunk cung cấp cho bạn một cách để đọc trạng thái hiện tại của cửa hàng Redux. Ngoài ra dispatch, nó cũng chuyển getStatelàm đối số thứ hai cho hàm bạn trả về từ trình tạo hành động thunk của bạn. Điều này cho phép thunk đọc trạng thái hiện tại của cửa hàng.

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

Đừng lạm dụng mô hình này. Sẽ tốt cho việc giải cứu các cuộc gọi API khi có sẵn dữ liệu được lưu trong bộ nhớ cache, nhưng nó không phải là nền tảng tốt để xây dựng logic kinh doanh của bạn. Nếu bạn getState()chỉ sử dụng để gửi các điều kiện khác nhau một cách có điều kiện, thay vào đó hãy xem xét đưa logic kinh doanh vào bộ giảm tốc.

Bước tiếp theo

Bây giờ bạn đã có một trực giác cơ bản về cách hoạt động của thunks, hãy xem ví dụ Redux async sử dụng chúng.

Bạn có thể tìm thấy nhiều ví dụ trong đó thunks trả lại Lời hứa. Điều này là không bắt buộc nhưng có thể rất thuận tiện. Redux không quan tâm những gì bạn trả lại từ một thunk, nhưng nó mang lại cho bạn giá trị trả về từ đó dispatch(). Đây là lý do tại sao bạn có thể trả lại một Lời hứa từ một thunk và chờ đợi nó hoàn thành bằng cách gọi dispatch(someThunkReturningPromise()).then(...).

Bạn cũng có thể chia người tạo hành động thunk phức tạp thành nhiều người tạo hành động thunk nhỏ hơn. Các dispatchphương pháp được cung cấp bởi thunks thể chấp nhận thunks chính nó, vì vậy bạn có thể áp dụng mô hình một cách đệ quy. Một lần nữa, điều này hoạt động tốt nhất với Promise vì bạn có thể triển khai luồng điều khiển không đồng bộ trên đó.

Đối với một số ứng dụng, bạn có thể thấy mình trong tình huống yêu cầu luồng điều khiển không đồng bộ của bạn quá phức tạp để được thể hiện bằng thunks. Ví dụ: thử lại các yêu cầu không thành công, luồng ủy quyền lại bằng mã thông báo hoặc việc đưa lên từng bước có thể quá dài dòng và dễ bị lỗi khi được viết theo cách này. Trong trường hợp này, bạn có thể muốn xem xét các giải pháp luồng điều khiển không đồng bộ nâng cao hơn như Redux Saga hoặc Redux Loop . Đánh giá chúng, so sánh các ví dụ phù hợp với nhu cầu của bạn và chọn một ví dụ bạn thích nhất.

Cuối cùng, không sử dụng bất cứ thứ gì (kể cả thunks) nếu bạn không có nhu cầu thực sự cho chúng. Hãy nhớ rằng, tùy thuộc vào yêu cầu, giải pháp của bạn có thể trông đơn giản như

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

Đừng đổ mồ hôi trừ khi bạn biết tại sao bạn lại làm việc này.


27
Các hành động không đồng bộ có vẻ như là một giải pháp đơn giản và thanh lịch cho một vấn đề phổ biến. Tại sao không hỗ trợ cho họ nướng vào để chuyển hướng mà không cần phần mềm trung gian? Câu trả lời này sau đó có thể ngắn gọn hơn rất nhiều.
Phil Mander

83
@PhilMander Bởi vì có nhiều mẫu thay thế như github.com/raisemarketplace/redux-loop hoặc github.com/yelouafi/redux-saga giống như (nếu không hơn) thanh lịch. Redux là một công cụ cấp thấp. Bạn có thể xây dựng một superset bạn thích và phân phối nó một cách riêng biệt.
Dan Abramov

16
Bạn có thể giải thích điều này: * xem xét đưa logic kinh doanh vào các bộ giảm tốc *, điều đó có nghĩa là tôi nên gửi một hành động, và sau đó xác định trong bộ giảm tốc những hành động tiếp theo để gửi tùy thuộc vào trạng thái của tôi? Câu hỏi của tôi là, sau đó tôi có gửi các hành động khác trực tiếp trong bộ giảm tốc của mình không, và nếu không thì tôi sẽ gửi chúng từ đâu?
froginvasion

25
Câu này chỉ áp dụng cho trường hợp đồng bộ. Ví dụ: nếu bạn viết if (cond) dispatch({ type: 'A' }) else dispatch({ type: 'B' })có lẽ bạn chỉ nên dispatch({ type: 'C', something: cond })và chọn bỏ qua hành động trong bộ giảm tốc thay vào đó tùy thuộc vào action.somethingvà trạng thái hiện tại.
Dan Abramov

29
@DanAbramov Bạn đã nhận được upvote của tôi chỉ vì điều này "Trừ khi bạn gặp vấn đề này, hãy sử dụng những gì ngôn ngữ cung cấp và đưa ra giải pháp đơn giản nhất." Chỉ sau khi tôi nhận ra ai đã viết nó!
Matt Lacey

189

Sử dụng Redux-saga

Như Dan Abramov đã nói, nếu bạn muốn kiểm soát nâng cao hơn đối với mã async của mình, bạn có thể xem qua redux-saga .

Câu trả lời này là một ví dụ đơn giản, nếu bạn muốn giải thích rõ hơn về lý do tại sao redux-saga có thể hữu ích cho ứng dụng của bạn, hãy kiểm tra câu trả lời khác này .

Ý tưởng chung là Redux-saga cung cấp trình thông dịch trình tạo ES6 cho phép bạn dễ dàng viết mã async trông giống như mã đồng bộ (đây là lý do tại sao bạn sẽ thường tìm thấy vô hạn trong khi các vòng lặp trong Redux-saga). Bằng cách nào đó, Redux-saga đang xây dựng ngôn ngữ của riêng mình ngay trong Javascript. Redux-saga ban đầu có thể cảm thấy hơi khó học, bởi vì bạn cần hiểu biết cơ bản về máy phát điện, nhưng cũng hiểu ngôn ngữ được cung cấp bởi Redux-saga.

Tôi sẽ thử ở đây để mô tả ở đây hệ thống thông báo mà tôi đã xây dựng dựa trên redux-saga. Ví dụ này hiện đang chạy trong sản xuất.

Đặc điểm kỹ thuật hệ thống thông báo nâng cao

  • Bạn có thể yêu cầu một thông báo sẽ được hiển thị
  • Bạn có thể yêu cầu một thông báo để ẩn
  • Một thông báo không nên được hiển thị quá 4 giây
  • Nhiều thông báo có thể được hiển thị cùng một lúc
  • Không thể hiển thị quá 3 thông báo cùng một lúc
  • Nếu một thông báo được yêu cầu trong khi đã có 3 thông báo được hiển thị, sau đó xếp hàng / hoãn thông báo.

Kết quả

Ảnh chụp màn hình ứng dụng sản xuất của tôi Stample.co

bánh mì nướng

Ở đây tôi đặt tên cho thông báo là a toastnhưng đây là một chi tiết đặt tên.

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

Và bộ giảm tốc:

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

Sử dụng

Bạn chỉ có thể gửi TOAST_DISPLAY_REQUESTEDcác sự kiện. Nếu bạn gửi 4 yêu cầu, chỉ có 3 thông báo sẽ được hiển thị và thông báo thứ 4 sẽ xuất hiện muộn hơn một chút sau khi thông báo thứ 1 biến mất.

Lưu ý rằng tôi không đặc biệt khuyên bạn nên gửi TOAST_DISPLAY_REQUESTEDtừ JSX. Bạn muốn thêm một câu chuyện khác lắng nghe các sự kiện ứng dụng đã có của bạn và sau đó gửi đi TOAST_DISPLAY_REQUESTED: thành phần của bạn kích hoạt thông báo, không cần phải kết hợp chặt chẽ với hệ thống thông báo.

Phần kết luận

Mã của tôi không hoàn hảo nhưng chạy trong sản xuất với 0 lỗi trong nhiều tháng. Redux-saga và máy phát điện ban đầu hơi khó khăn nhưng một khi bạn hiểu chúng thì loại hệ thống này khá dễ xây dựng.

Thậm chí khá dễ dàng để thực hiện các quy tắc phức tạp hơn, như:

  • khi có quá nhiều thông báo được "xếp hàng", hãy dành ít thời gian hiển thị hơn cho mỗi thông báo để kích thước hàng đợi có thể giảm nhanh hơn.
  • phát hiện thay đổi kích thước cửa sổ và thay đổi số lượng thông báo hiển thị tối đa tương ứng (ví dụ: máy tính để bàn = 3, chân dung điện thoại = 2, cảnh quan điện thoại = 1)

Thành thật mà nói, chúc may mắn thực hiện loại công cụ này đúng cách với thunks.

Lưu ý rằng bạn có thể thực hiện chính xác cùng một loại điều với redux-obsable rất giống với redux-saga. Nó gần giống nhau và là vấn đề về hương vị giữa máy phát điện và RxJS.


18
Tôi ước câu trả lời của bạn đến sớm hơn khi câu hỏi được hỏi, vì tôi không thể đồng ý nhiều hơn với việc sử dụng thư viện hiệu ứng phụ Saga cho logic kinh doanh như thế này. Reducers & Action Creators dành cho chuyển trạng thái. Quy trình công việc không giống như các chức năng chuyển trạng thái. Quy trình làm việc bước qua các chuyển đổi, nhưng bản thân chúng không phải là quá trình chuyển đổi. Redux + React thiếu điều này một mình - đây chính là lý do tại sao Redux Saga rất hữu ích.
Atticus

4
Cảm ơn, tôi cố gắng hết sức để làm cho redux-saga trở nên phổ biến vì những lý do này :) quá ít người nghĩ hiện tại redux-saga chỉ là sự thay thế cho thunks và không thấy redux-saga cho phép quy trình làm việc phức tạp và tách rời
Sebastien Lorber

1
Chính xác. Hành động & Giảm là tất cả các phần của máy trạng thái. Đôi khi, đối với các quy trình công việc phức tạp, bạn cần một cái gì đó khác để sắp xếp bộ máy trạng thái không trực tiếp là một phần của bộ máy trạng thái!
Atticus

2
Hành động: Tải trọng / sự kiện sang trạng thái chuyển tiếp. Giảm: Chức năng chuyển trạng thái. Thành phần: Giao diện người dùng phản ánh trạng thái. Nhưng còn thiếu một phần quan trọng - làm thế nào để bạn quản lý quá trình chuyển đổi mà tất cả đều có logic riêng để xác định chuyển đổi nào sẽ thực hiện tiếp theo? Redux Saga!
Atticus

2
@mrbrdo nếu bạn đọc kỹ câu trả lời của tôi, bạn sẽ nhận thấy rằng thời gian chờ thông báo thực sự được xử lý với yield call(delay,timeoutValue);: đó không phải là cùng một API nhưng nó có tác dụng tương tự
Sebastien Lorber

25

Một kho lưu trữ với các dự án mẫu

Hiện tại có bốn dự án mẫu:

  1. Viết nội tuyến mã Async
  2. Trích xuất trình tạo hành động Async
  3. Sử dụng Redux Thunk
  4. Sử dụng Redux Saga

Câu trả lời được chấp nhận là tuyệt vời.

Nhưng còn thiếu một thứ:

  1. Không có dự án mẫu có thể chạy được, chỉ có một số đoạn mã.
  2. Không có mã mẫu cho các lựa chọn thay thế khác, chẳng hạn như:
    1. Redux Saga

Vì vậy, tôi đã tạo kho lưu trữ Hello Async để thêm những thứ còn thiếu:

  1. Dự án runnable. Bạn có thể tải xuống và chạy chúng mà không cần sửa đổi.
  2. Cung cấp mã mẫu cho nhiều lựa chọn thay thế:

Redux Saga

Câu trả lời được chấp nhận đã cung cấp các đoạn mã mẫu cho Nội tuyến mã Async, Trình tạo hành động Async và Redux Thunk. Để hoàn thiện, tôi cung cấp các đoạn mã cho 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 }
}

Hành động rất đơn giản và tinh khiết.

// component.js

import { connect } from 'react-redux'

// ...

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

// ...

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

Không có gì đặc biệt với thành phần.

// 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

Sagas dựa trên ES6 Generators

// index.js

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

const sagaMiddleware = createSagaMiddleware()

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

sagaMiddleware.run(saga)

So với Redux Thunk

Ưu

  • Bạn không kết thúc trong địa ngục gọi lại.
  • Bạn có thể kiểm tra các luồng không đồng bộ của mình một cách dễ dàng.
  • Hành động của bạn vẫn trong sạch.

Nhược điểm

  • Nó phụ thuộc vào ES6 Generators tương đối mới.

Vui lòng tham khảo dự án có thể chạy được nếu đoạn mã trên không trả lời tất cả các câu hỏi của bạn.


23

Bạn có thể làm điều này với redux-thunk . Có một hướng dẫn trong tài liệu redux cho các hành động không đồng bộ như setTimeout.


Chỉ là một câu hỏi tiếp theo nhanh chóng, khi sử dụng phần mềm trung gian, applyMiddleware(ReduxPromise, thunk)(createStore)đây là cách bạn thêm một số phần mềm trung gian (có bị tách rời?) Vì tôi dường như không thể làm việc được.
Ilja

1
@Ilja Điều này sẽ hoạt động:const store = createStore(reducer, applyMiddleware([ReduxPromise, thunk]));
geniuscarrier

22

Tôi cũng khuyên bạn nên xem mẫu SAM .

Mẫu SAM ủng hộ việc bao gồm "vị ngữ hành động tiếp theo" trong đó các hành động (tự động) như "thông báo tự động biến mất sau 5 giây" được kích hoạt sau khi mô hình được cập nhật (mô hình SAM ~ trạng thái giảm + lưu trữ).

Mẫu ủng hộ việc sắp xếp các hành động và đột biến mô hình một lần, bởi vì "trạng thái điều khiển" của mô hình "điều khiển" hành động nào được kích hoạt và / hoặc tự động thực hiện bởi vị từ hành động tiếp theo. Bạn chỉ đơn giản là không thể dự đoán (nói chung) hệ thống sẽ ở trạng thái nào trước khi xử lý một hành động và do đó liệu hành động dự kiến ​​tiếp theo của bạn có được phép / có thể hay không.

Vì vậy, ví dụ mã,

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

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

sẽ không được phép với SAM, vì thực tế là một hành động ẩnNotification có thể được gửi đi phụ thuộc vào mô hình chấp nhận thành công giá trị "showNotication: true". Có thể có các phần khác của mô hình ngăn không cho nó chấp nhận nó và do đó, sẽ không có lý do gì để kích hoạt hành động ẩnNotification.

Tôi đặc biệt khuyên bạn nên triển khai một vị từ hành động tiếp theo thích hợp sau khi cửa hàng cập nhật và trạng thái điều khiển mới của mô hình có thể được biết. Đó là cách an toàn nhất để thực hiện hành vi bạn đang tìm kiếm.

Bạn có thể tham gia với chúng tôi trên Gitter nếu bạn muốn. Ngoài ra còn có một hướng dẫn bắt đầu SAM có sẵn ở đây .


Tôi mới chỉ bị trầy xước bề mặt, nhưng đã bị mô hình SAM làm cho xúc động. V = S( vm( M.present( A(data) ) ), nap(M))chỉ là đẹp Cảm ơn đã chia sẻ suy nghĩ và kinh nghiệm của bạn. Tôi sẽ đào sâu hơn.

@ftor, cảm ơn bạn! Khi tôi viết nó lần đầu tiên, tôi cũng có cảm giác tương tự. Tôi đã sử dụng SAM trong sản xuất gần một năm nay và tôi không thể nghĩ đến lúc tôi cảm thấy mình cần một thư viện để thực hiện SAM (ngay cả vdom, mặc dù tôi có thể thấy khi nào nó có thể được sử dụng). Chỉ cần một dòng mã, vậy thôi! SAM tạo ra mã đẳng cấu, không có sự mơ hồ trong đó làm thế nào để xử lý các cuộc gọi không đồng bộ ... Tôi không thể nghĩ về một thời điểm mà tôi mặc dù, tôi đang làm gì?
metaprogrammer

SAM là một mẫu Kỹ thuật phần mềm thực sự (vừa sản xuất SDK Alexa với nó). Nó dựa trên TLA + và cố gắng mang lại sức mạnh của công việc đáng kinh ngạc đó cho mọi nhà phát triển. SAM sửa ba phép tính gần đúng mà mọi người đã sử dụng trong nhiều thập kỷ: - các hành động có thể điều khiển trạng thái ứng dụng - các bài tập tương đương với đột biến - không có định nghĩa chính xác về bước lập trình là gì (ví dụ: a = b * ca step , là 1 / đọc b, c 2 / tính b * c, 3 / gán a với kết quả ba bước khác nhau?
metaprogrammer

20

Sau khi thử các cách tiếp cận phổ biến khác nhau (người tạo hành động, thunks, sagas, sử thi, hiệu ứng, phần mềm trung gian tùy chỉnh), tôi vẫn cảm thấy rằng có thể có chỗ để cải thiện nên tôi đã ghi lại hành trình của mình trong bài viết trên blog này, tôi sẽ đưa logic kinh doanh của mình vào đâu một ứng dụng React / Redux?

Giống như các cuộc thảo luận ở đây, tôi đã cố gắng đối chiếu và so sánh các cách tiếp cận khác nhau. Cuối cùng, nó đã dẫn tôi đến việc giới thiệu một thư viện logic mới , lấy cảm hứng từ sử thi, sagas, phần mềm trung gian tùy chỉnh.

Nó cho phép bạn chặn các hành động để xác thực, xác minh, ủy quyền cũng như cung cấp cách thực hiện IO không đồng bộ.

Một số chức năng phổ biến có thể được khai báo đơn giản như gỡ lỗi, điều chỉnh, hủy bỏ và chỉ sử dụng phản hồi từ yêu cầu mới nhất (TakeLatest). redux-logic kết thúc mã của bạn cung cấp chức năng này cho bạn.

Điều đó giải phóng bạn để thực hiện logic kinh doanh cốt lõi của bạn theo cách bạn muốn. Bạn không phải sử dụng thiết bị quan sát hoặc máy phát điện trừ khi bạn muốn. Sử dụng các chức năng và gọi lại, lời hứa, chức năng không đồng bộ (async / await), v.v.

Mã để thực hiện một thông báo 5s đơn giản sẽ giống như:

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

Tôi có một ví dụ thông báo nâng cao hơn trong repo của tôi hoạt động tương tự như những gì Sebastian Lorber đã mô tả nơi bạn có thể giới hạn hiển thị ở N mục và xoay qua bất kỳ thứ gì được xếp hàng. ví dụ thông báo logic redux

Tôi có một loạt các ví dụ trực tiếp jsfiddle logic cũng như các ví dụ đầy đủ . Tôi đang tiếp tục làm việc trên các tài liệu và ví dụ.

Tôi rất thích nghe phản hồi của bạn.


Tôi không chắc chắn rằng tôi thích thư viện của bạn nhưng tôi thích bài viết của bạn! Người đàn ông tốt! Bạn đã làm đủ việc để tiết kiệm thời gian của người khác.
Tyler Long

2
Tôi đã tạo một dự án mẫu cho redux-logic tại đây: github.com/tylerlong/hello-async/tree/master/redux-logic Tôi nghĩ rằng đó là một phần mềm được thiết kế tốt và tôi không thấy bất kỳ nhược điểm lớn nào so với các phần mềm khác lựa chọn thay thế.
Tyler Long

9

Tôi hiểu rằng câu hỏi này hơi cũ nhưng tôi sẽ giới thiệu một giải pháp khác bằng cách sử dụng redux-obsable aka. Sử thi.

Trích dẫn tài liệu chính thức:

Redux có thể quan sát được là gì?

Phần mềm trung gian dựa trên RxJS 5 cho Redux. Soạn và hủy các hành động không đồng bộ để tạo hiệu ứng phụ và hơn thế nữa.

Một Epic là nguyên thủy cốt lõi của redux - có thể quan sát được.

Đây là một chức năng có một luồng hành động và trả về một luồng hành động. Hành động trong, hành động ra.

Trong ít nhiều từ, bạn có thể tạo một hàm nhận hành động thông qua Luồng và sau đó trả về luồng hành động mới (sử dụng các tác dụng phụ phổ biến như thời gian chờ, độ trễ, khoảng thời gian và yêu cầu).

Hãy để tôi đăng mã và sau đó giải thích thêm một chút về nó

store.js

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.js

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)

Mã khóa để giải quyết vấn đề này dễ như ăn bánh như bạn có thể thấy, điều duy nhất xuất hiện khác với các câu trả lời khác là hàm rootEpic.

Điểm 1. Giống như sagas, bạn phải kết hợp các sử thi để có được chức năng cấp cao nhất nhận luồng hành động và trả về luồng hành động, do đó bạn có thể sử dụng nó với nhà máy trung gian tạoEpicMiddleware . Trong trường hợp của chúng tôi, chúng tôi chỉ cần một vì vậy chúng tôi chỉ có rootEpic của chúng tôi vì vậy chúng tôi không phải kết hợp bất cứ điều gì nhưng thật tốt khi biết điều đó.

Điểm 2. RootEpic của chúng tôi quan tâm đến logic tác dụng phụ chỉ mất khoảng 5 dòng mã, thật tuyệt vời! Bao gồm cả thực tế là khai báo khá nhiều!

Điểm 3. Từng dòng giải thích rootEpic (trong phần bình luận)

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

Tôi hy vọng nó sẽ giúp!


Bạn có thể giải thích những gì các phương pháp api cụ thể đang làm ở đây, chẳng hạn như switchMap?
Dmitri Zaitsev

1
Chúng tôi đang sử dụng redux-có thể quan sát được trong ứng dụng React Native của chúng tôi trên Windows. Đó là một giải pháp triển khai thanh lịch cho một vấn đề phức tạp, không đồng bộ cao và có sự hỗ trợ tuyệt vời thông qua các vấn đề về kênh Gitter và GitHub của họ. Lớp phức tạp thêm chỉ có giá trị nếu bạn đi đến vấn đề chính xác mà nó có nghĩa là để giải quyết, tất nhiên.
Matt Hargett

8

Tại sao nó phải khó như vậy? Đó chỉ là logic UI. Sử dụng một hành động chuyên dụng để đặt dữ liệu thông báo:

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

và một thành phần chuyên dụng để hiển thị nó:

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

Trong trường hợp này, các câu hỏi phải là "làm thế nào để bạn dọn sạch trạng thái cũ?", "Làm thế nào để thông báo cho một thành phần mà thời gian đã thay đổi"

Bạn có thể thực hiện một số hành động TIMEOUT được gửi trên setTimeout từ một thành phần.

Có lẽ nó chỉ tốt để làm sạch nó bất cứ khi nào một thông báo mới được hiển thị.

Dù sao, nên có một số setTimeoutnơi, phải không? Tại sao không làm điều đó trong một thành phần

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

Động lực là chức năng "thông báo mờ dần" thực sự là mối quan tâm của UI. Vì vậy, nó đơn giản hóa thử nghiệm cho logic kinh doanh của bạn.

Nó dường như không có ý nghĩa để kiểm tra cách nó được thực hiện. Nó chỉ có ý nghĩa để xác minh khi hết thời gian thông báo. Do đó ít mã hơn để sơ khai, kiểm tra nhanh hơn, mã sạch hơn.


1
Đây phải là câu trả lời hàng đầu.
mmla

6

Nếu bạn muốn xử lý thời gian chờ trên các hành động chọn lọc, bạn có thể thử phần mềm trung gian phương pháp . Tôi đã đối mặt với một vấn đề tương tự để xử lý các hành động dựa trên lời hứa một cách có chọn lọc và giải pháp này linh hoạt hơn.

Hãy nói với bạn rằng người tạo hành động của bạn trông như thế này:

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

thời gian chờ có thể giữ nhiều giá trị trong hành động trên

  • số tính bằng ms - trong khoảng thời gian chờ cụ thể
  • đúng - trong một khoảng thời gian chờ không đổi. (xử lý trong phần mềm trung gian)
  • không xác định - cho công văn ngay lập tức

Việc triển khai phần mềm trung gian của bạn sẽ như thế này:

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

Bây giờ bạn có thể định tuyến tất cả các hành động của mình thông qua lớp phần mềm trung gian này bằng cách sử dụng redux.

createStore(reducer, applyMiddleware(timeoutMiddleware))

Bạn có thể tìm thấy một số ví dụ tương tự ở đây


5

Cách thích hợp để làm điều này là sử dụng Redux Thunk , một phần mềm trung gian phổ biến cho Redux, theo tài liệu của Redux Thunk:

"Redux Thunk middleware cho phép bạn viết các trình tạo hành động trả về một hàm thay vì một hành động. Thunk có thể được sử dụng để trì hoãn việc gửi một hành động hoặc chỉ gửi nếu một điều kiện nhất định được đáp ứng. Hàm bên trong nhận được các phương thức lưu trữ gửi và getState làm tham số ".

Vì vậy, về cơ bản, nó trả về một hàm và bạn có thể trì hoãn công văn của mình hoặc đặt nó ở trạng thái điều kiện.

Vì vậy, một cái gì đó như thế này sẽ làm công việc cho bạn:

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

4

Nó đơn giản. Sử dụng gói trim-redux và viết như thế này ở componentDidMountnơi khác hoặc giết nó componentWillUnmount.

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

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

3

Bản thân Redux là một thư viện dài dòng và đối với những thứ như vậy, bạn sẽ phải sử dụng một cái gì đó như Redux-thunk , sẽ cung cấp một dispatchchức năng, do đó bạn sẽ có thể gửi thông báo đóng sau vài giây.

Tôi đã tạo một thư viện để giải quyết các vấn đề như tính dài dòng và khả năng kết hợp, và ví dụ của bạn sẽ giống như sau:

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],
});

Vì vậy, chúng tôi soạn thảo các hành động đồng bộ hóa để hiển thị thông báo bên trong hành động không đồng bộ, có thể yêu cầu một số thông tin nền hoặc kiểm tra xem liệu thông báo đã được đóng bằng tay chưa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.