Giới thiệu về Redux Saga

Redux Saga là một thư viện dùng để xử lý các hiệu ứng phụ trong Redux. Khi bạn kích hoạt một hành động, điều gì đó sẽ thay đổi trong trạng thái của ứng dụng và bạn có thể cần phải làm điều gì đó bắt nguồn từ sự thay đổi trạng thái này

Khi nào sử dụng Redux Saga

Trong một ứng dụng sử dụngRedux, khi bạn kích hoạt một hành động, trạng thái của ứng dụng sẽ thay đổi.

Khi điều này xảy ra, bạn có thể cần phải làm gì đó bắt nguồn từ sự thay đổi trạng thái này.

Ví dụ, bạn có thể muốn:

  • thực hiện một cuộc gọi HTTP đến một máy chủ
  • gửi một sự kiện WebSocket
  • tìm nạp một số dữ liệu từ mộtGraphQLngười phục vụ
  • lưu một cái gì đó vào bộ nhớ cache hoặc bộ nhớ cục bộ của trình duyệt

… Bạn có ý tưởng.

Đó là tất cả những thứ không thực sự liên quan đến trạng thái ứng dụng hoặc không đồng bộ và bạn cần chuyển chúng vào một nơi khác với hành động hoặc bộ giảm của bạn (trong khi về mặt kỹ thuật, bạncó thể, đó không phải là một cách tốt để có một cơ sở mã sạch).

Nhập Redux Saga, một phần mềm trung gian của Redux giúp bạn giải quyết các tác dụng phụ.

Ví dụ cơ bản về cách sử dụng Redux Saga

Để tránh đi sâu vào lý thuyết quá nhiều trước khi hiển thị một số mã thực tế, tôi trình bày ngắn gọn cách tôi giải quyết một vấn đề mà tôi gặp phải khi xây dựng một ứng dụng mẫu.

Trong phòng trò chuyện, khi người dùng viết tin nhắn, tôi ngay lập tức hiển thị tin nhắn đó trên màn hình để đưa ra phản hồi nhanh chóng. Điều này được thực hiện thông qua mộtHành động Redux:

const addMessage = (message, author) => ({
  type: 'ADD_MESSAGE',
  message,
  author
})

và trạng thái được thay đổi thông qua một bộ giảm tốc:

const messages = (state = [], action) => {
  switch (action.type) {
    case 'ADD_MESSAGE':
      return state.concat([{
        message: action.message,
        author: action.author
      }])
    default:
      return state
  }
}

Bạn khởi tạo Redux Saga bằng cách nhập nó trước, sau đó áp dụngsaganhư một phần mềm trung gian cho Redux Store:

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

Sau đó, chúng tôi tạo một phần mềm trung gian và áp dụng nó vào Redux Store mới tạo của chúng tôi:

const sagaMiddleware = createSagaMiddleware()

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

Bước cuối cùng là chạy saga. Chúng tôi nhập nó và chuyển nó vào phương thức chạy của phần mềm trung gian:

import handleNewMessage from './sagas'
//...
sagaMiddleware.run(handleNewMessage)

Chúng ta chỉ cần viết câu chuyện, trong./sagas/index.js:

import { takeEvery } from 'redux-saga/effects'

const handleNewMessage = function* handleNewMessage(params) { const socket = new WebSocket(‘ws://localhost:8989’) yield takeEvery(‘ADD_MESSAGE’, (action) => { socket.send(JSON.stringify(action)) }) }

export default handleNewMessage

Ý nghĩa của mã này là:mỗi lầncácADD_MESSAGEhành động kích hoạt, chúng tôi gửi một tin nhắn đếnWebSocketsmáy chủ, phản hồi trong trường hợp này trênlocalhost:8989.

Lưu ý việc sử dụngfunction*, đây không phải là một chức năng bình thường, nhưngmáy phát điện.

Cách nó hoạt động đằng sau hậu trường

Là mộtReduxPhần mềm trung gian, Redux Saga có thể chặn Redux Actions và đưa vào chức năng của riêng nó.

Có một vài khái niệm cần nắm bắt, và đây là những từ khóa chính mà bạn sẽ muốn ghi lại trong đầu mình:saga,máy phát điện,phần mềm trung gian,lời hứa,tạm ngừng,sơ yếu lý lịch,hiệu ứng,cử đi,hoạt động,hoàn thành,đã giải quyết,năng suất,nhường nhịn.

Asagalà một số "câu chuyện" phản ứng với mộthiệu ứngmà mã của bạn đang gây ra. Điều đó có thể chứa một trong những điều chúng ta đã nói trước đây, như một yêu cầu HTTP hoặc một số thủ tục lưu vào bộ nhớ cache.

Chúng tôi tạo ra mộtphần mềm trung gianvới một danh sáchsagasđể chạy, có thể là một hoặc nhiều và chúng tôi kết nối phần mềm trung gian này với cửa hàng Redux.

Asagalà mộtmáy phát điệnchức năng. Khi mộtlời hứađược chạy vànhường nhịn, phần mềm trung gianđình chỉcácsagacho đên khilời hứađã giải quyết.

Một khilời hứađã giải quyếtphần mềm trung giansơ yếu lý lịchcâu chuyện, cho đến phần tiếp theonăng suấttuyên bố được tìm thấy, và nó ở đóbị đình chỉmột lần nữa cho đến khi của nólời hứa giải quyết.

Bên trong mã saga, bạn sẽ tạoCác hiệu ứngsử dụng một số chức năng trợ giúp đặc biệt được cung cấp bởiredux-sagagói hàng. Để bắt đầu, chúng ta có thể liệt kê:

  • takeEvery()
  • takeLatest()
  • take()
  • call()
  • put()

Khi mộthiệu ứngđược thực hiện,sagatạm dừngcho đên khihiệu ứnghoàn thành.

Ví dụ:

import { takeEvery } from 'redux-saga/effects'

const handleNewMessage = function* handleNewMessage(params) { const socket = new WebSocket(‘ws://localhost:8989’) yield takeEvery(‘ADD_MESSAGE’, (action) => { socket.send(JSON.stringify(action)) }) }

export default handleNewMessage

Khi màphần mềm trung gianthực hiệnhandleNewMessagesaga, nódừng lạitạiyield takeEveryhướng dẫn vàchờ đợi(không đồng bộ, tất nhiên) cho đến khiADD_MESSAGEhành động làcử đi. Sau đó, nó chạy lệnh gọi lại vàsagacó thểsơ yếu lý lịch.

Người trợ giúp cơ bản

Người trợ giúp là phần tóm tắt trên các API saga cấp thấp.

Hãy giới thiệu những trợ giúp cơ bản nhất mà bạn có thể sử dụng để chạy các hiệu ứng của mình:

  • takeEvery()
  • takeLatest()
  • take()
  • put()
  • call()

takeEvery()

takeEvery(), được sử dụng trong một số ví dụ, là một trong những trợ giúp đó.

Trong mã:

import { takeEvery } from 'redux-saga/effects'

function* watchMessages() { yield takeEvery(‘ADD_MESSAGE’, postMessageToServer) }

CácwatchMessagesmáy phát điện tạm dừng cho đến khiADD_MESSAGEvụ cháy hành động, vàmỗi lầnnó cháy, nó sẽ gọipostMessageToServerchức năng, vô hạn và đồng thời (không cầnpostMessageToServerchấm dứt thực thi của nó trước khi một lần mới có thể chạy)

takeLatest()

Một người trợ giúp phổ biến khác làtakeLatest(), rất giống vớitakeEvery()nhưng chỉ cho phép một trình xử lý hàm chạy tại một thời điểm, tránh đồng thời. Nếu một hành động khác được kích hoạt khi trình xử lý vẫn đang chạy, nó sẽ hủy hành động đó và chạy lại với dữ liệu mới nhất có sẵn.

Như vớitakeEvery(), bộ tạo không bao giờ dừng và tiếp tục chạy hiệu ứng khi hành động được chỉ định xảy ra.

take()

take()khác ở chỗ nó chỉ đợi một lần duy nhất. Khi hành động mà nó đang chờ xảy ra, lời hứa sẽ giải quyết và trình lặp được tiếp tục, vì vậy nó có thể chuyển sang tập lệnh tiếp theo.

put()

Gửi một hành động đến cửa hàng Redux. Thay vì chuyển vào cửa hàng Redux hoặc hành động gửi đến saga, bạn chỉ có thể sử dụngput():

yield put({ type: 'INCREMENT' })
yield put({ type: "USER_FETCH_SUCCEEDED", data: data })

sẽ trả về một đối tượng đơn giản mà bạn có thể dễ dàng kiểm tra trong các thử nghiệm của mình (thêm về thử nghiệm sau).

call()

Khi bạn muốn gọi một số hàm trong saga, bạn có thể làm như vậy bằng cách sử dụng lệnh gọi hàm thuần túy có kết quả trả về một lời hứa:

delay(1000)

nhưng điều này không tốt với các bài kiểm tra. Thay thế,call()cho phép bạn gói lời gọi hàm đó và trả về một đối tượng có thể dễ dàng kiểm tra:

call(delay, 1000)

trả lại

{ CALL: {fn: delay, args: [1000]}}

Chạy các hiệu ứng song song

Có thể chạy các hiệu ứng song song bằng cách sử dụngall()race(), rất khác nhau về những gì họ làm.

all()

Nếu bạn viết

import { call } from 'redux-saga/effects'

const todos = yield call(fetch, ‘/api/todos’) const user = yield call(fetch, ‘/api/user’)

thư haifetch()cuộc gọi sẽ không được thực hiện cho đến khi cuộc gọi đầu tiên thành công.

Để thực hiện chúng song song, hãy quấn chúng vàoall():

import { all, call } from 'redux-saga/effects'

const [todos, user] = yield all([ call(fetch, ‘/api/todos’), call(fetch, ‘/api/user’) ])

all()sẽ không được giải quyết cho đến khi cả haicall()trở về.

race()

race()khác vớiall()bằng cách không đợi tất cả các cuộc gọi của người trợ giúp quay lại. Nó chỉ đợi một người quay trở lại, và nó đã hoàn tất.

Đó là một cuộc đua để xem ai về đích trước, và sau đó chúng ta quên đi những người tham gia khác.

Nó thường được sử dụng để hủy một tác vụ nền chạy mãi mãi cho đến khi có điều gì đó xảy ra:

import { race, call, take } from 'redux-saga/effects'

function* someBackgroundTask() { while(1) { //… } }

yield race([ bgTask: call(someBackgroundTask), cancel: take(‘CANCEL_TASK’) ])

khi màCANCEL_TASKhành động được phát ra, chúng tôi dừng tác vụ khác mà nếu không sẽ chạy mãi mãi.

Tải xuống miễn phí của tôiSổ tay React


Các hướng dẫn về phản ứng khác: