Tại sao chúng ta cần phần mềm trung gian cho luồng không đồng bộ trong Redux?


684

Theo các tài liệu, "Không có phần mềm trung gian, cửa hàng Redux chỉ hỗ trợ luồng dữ liệu đồng bộ" . Tôi không hiểu tại sao lại như vậy. Tại sao thành phần container không thể gọi API async và sau đó dispatchlà các hành động?

Ví dụ, hãy tưởng tượng một giao diện người dùng đơn giản: một trường và một nút. Khi người dùng nhấn nút, trường sẽ được điền dữ liệu từ một máy chủ từ xa.

Một trường và một nút

import * as React from 'react';
import * as Redux from 'redux';
import { Provider, connect } from 'react-redux';

const ActionTypes = {
    STARTED_UPDATING: 'STARTED_UPDATING',
    UPDATED: 'UPDATED'
};

class AsyncApi {
    static getFieldValue() {
        const promise = new Promise((resolve) => {
            setTimeout(() => {
                resolve(Math.floor(Math.random() * 100));
            }, 1000);
        });
        return promise;
    }
}

class App extends React.Component {
    render() {
        return (
            <div>
                <input value={this.props.field}/>
                <button disabled={this.props.isWaiting} onClick={this.props.update}>Fetch</button>
                {this.props.isWaiting && <div>Waiting...</div>}
            </div>
        );
    }
}
App.propTypes = {
    dispatch: React.PropTypes.func,
    field: React.PropTypes.any,
    isWaiting: React.PropTypes.bool
};

const reducer = (state = { field: 'No data', isWaiting: false }, action) => {
    switch (action.type) {
        case ActionTypes.STARTED_UPDATING:
            return { ...state, isWaiting: true };
        case ActionTypes.UPDATED:
            return { ...state, isWaiting: false, field: action.payload };
        default:
            return state;
    }
};
const store = Redux.createStore(reducer);
const ConnectedApp = connect(
    (state) => {
        return { ...state };
    },
    (dispatch) => {
        return {
            update: () => {
                dispatch({
                    type: ActionTypes.STARTED_UPDATING
                });
                AsyncApi.getFieldValue()
                    .then(result => dispatch({
                        type: ActionTypes.UPDATED,
                        payload: result
                    }));
            }
        };
    })(App);
export default class extends React.Component {
    render() {
        return <Provider store={store}><ConnectedApp/></Provider>;
    }
}

Khi thành phần xuất được hiển thị, tôi có thể nhấp vào nút và đầu vào được cập nhật chính xác.

Lưu ý updatechức năng trong connectcuộc gọi. Nó gửi một hành động cho Ứng dụng biết rằng nó đang cập nhật và sau đó thực hiện cuộc gọi không đồng bộ. Sau khi cuộc gọi kết thúc, giá trị được cung cấp sẽ được gửi dưới dạng tải trọng của một hành động khác.

Điều gì là sai với phương pháp này? Tại sao tôi muốn sử dụng Redux Thunk hoặc Redux Promise, như tài liệu cho thấy?

EDIT: Tôi đã tìm kiếm repo Redux để tìm manh mối và thấy rằng Action Creators được yêu cầu phải là các hàm thuần túy trong quá khứ. Ví dụ: đây là người dùng đang cố gắng cung cấp giải thích tốt hơn cho luồng dữ liệu không đồng bộ:

Bản thân người tạo hành động vẫn là một hàm thuần túy, nhưng hàm thunk mà nó trả về không cần phải có và nó có thể thực hiện các cuộc gọi async của chúng tôi

Người tạo hành động không còn cần phải thuần khiết. Vì vậy, phần mềm trung gian thunk / lời hứa chắc chắn là cần thiết trong quá khứ, nhưng có vẻ như điều này không còn là trường hợp nữa?


53
Người tạo hành động không bao giờ được yêu cầu là chức năng thuần túy. Đó là một sai lầm trong các tài liệu, không phải là một quyết định thay đổi.
Dan Abramov

1
@DanAbramov cho khả năng kiểm tra tuy nhiên nó có thể là một thực hành tốt. Redux-saga cho phép điều này: stackoverflow.com/a/34623840/82609
Sebastien Lorber

Câu trả lời:


697

Điều gì là sai với phương pháp này? Tại sao tôi muốn sử dụng Redux Thunk hoặc Redux Promise, như tài liệu cho thấy?

Không có gì sai với phương pháp này. Nó chỉ bất tiện trong một ứng dụng lớn vì bạn sẽ có các thành phần khác nhau thực hiện cùng một hành động, bạn có thể muốn gỡ lỗi một số hành động hoặc giữ một số trạng thái cục bộ như ID tăng tự động gần với người tạo hành động, v.v. quan điểm bảo trì để trích xuất các trình tạo hành động thành các chức năng riêng biệt.

Bạn có thể đọc câu trả lời của tôi cho Hướng dẫn cách gửi một hành động Redux với thời gian chờ trực tiếp để có hướng dẫn chi tiết hơn.

Các phần mềm trung gian như Redux Thunk hoặc Redux Promise chỉ cung cấp cho bạn cú pháp Sugar đường để gửi thunks hoặc lời hứa, nhưng bạn không phải sử dụng nó.

Vì vậy, không có bất kỳ phần mềm trung gian nào, người tạo hành động của bạn có thể trông giống như

// action creator
function loadData(dispatch, userId) { // needs to dispatch, so it is first argument
  return fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_DATA_FAILURE', err })
    );
}

// component
componentWillMount() {
  loadData(this.props.dispatch, this.props.userId); // don't forget to pass dispatch
}

Nhưng với Thunk Middleware bạn có thể viết nó như thế này:

// action creator
function loadData(userId) {
  return dispatch => fetch(`http://data.com/${userId}`) // Redux Thunk handles these
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_DATA_FAILURE', err })
    );
}

// component
componentWillMount() {
  this.props.dispatch(loadData(this.props.userId)); // dispatch like you usually do
}

Vì vậy, không có sự khác biệt lớn. Một điều tôi thích về cách tiếp cận thứ hai là thành phần không quan tâm rằng trình tạo hành động không đồng bộ. Nó chỉ gọi dispatchbình thường, nó cũng có thể sử dụng mapDispatchToPropsđể liên kết người tạo hành động đó bằng một cú pháp ngắn, v.v. ) mà không thay đổi các thành phần. Mặt khác, với cách tiếp cận rõ ràng trước đây, các thành phần của bạn biết chính xác rằng một cuộc gọi cụ thể là không đồng bộ và cần dispatchđược thông qua bởi một số quy ước (ví dụ: như một tham số đồng bộ hóa).

Cũng nghĩ về cách mã này sẽ thay đổi. Giả sử chúng tôi muốn có chức năng tải dữ liệu thứ hai và kết hợp chúng trong một trình tạo hành động duy nhất.

Với cách tiếp cận đầu tiên, chúng ta cần lưu ý về loại người tạo hành động mà chúng ta đang gọi:

// action creators
function loadSomeData(dispatch, userId) {
  return fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
    );
}
function loadOtherData(dispatch, userId) {
  return fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_OTHER_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_OTHER_DATA_FAILURE', err })
    );
}
function loadAllData(dispatch, userId) {
  return Promise.all(
    loadSomeData(dispatch, userId), // pass dispatch first: it's async
    loadOtherData(dispatch, userId) // pass dispatch first: it's async
  );
}


// component
componentWillMount() {
  loadAllData(this.props.dispatch, this.props.userId); // pass dispatch first
}

Với Redux Thunk, người tạo hành động có thể dispatchlà kết quả của những người tạo hành động khác và thậm chí không nghĩ liệu đó là những người đồng bộ hay không đồng bộ:

// action creators
function loadSomeData(userId) {
  return dispatch => fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
    );
}
function loadOtherData(userId) {
  return dispatch => fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_OTHER_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_OTHER_DATA_FAILURE', err })
    );
}
function loadAllData(userId) {
  return dispatch => Promise.all(
    dispatch(loadSomeData(userId)), // just dispatch normally!
    dispatch(loadOtherData(userId)) // just dispatch normally!
  );
}


// component
componentWillMount() {
  this.props.dispatch(loadAllData(this.props.userId)); // just dispatch normally!
}

Với cách tiếp cận này, nếu sau này bạn muốn người tạo hành động của mình xem xét trạng thái Redux hiện tại, bạn chỉ có thể sử dụng getStateđối số thứ hai được truyền cho thunks mà không cần sửa đổi mã cuộc gọi:

function loadSomeData(userId) {
  // Thanks to Redux Thunk I can use getState() here without changing callers
  return (dispatch, getState) => {
    if (getState().data[userId].isLoaded) {
      return Promise.resolve();
    }

    fetch(`http://data.com/${userId}`)
      .then(res => res.json())
      .then(
        data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
        err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
      );
  }
}

Nếu bạn cần thay đổi nó thành đồng bộ, bạn cũng có thể thực hiện việc này mà không thay đổi bất kỳ mã gọi nào:

// I can change it to be a regular action creator without touching callers
function loadSomeData(userId) {
  return {
    type: 'LOAD_SOME_DATA_SUCCESS',
    data: localStorage.getItem('my-data')
  }
}

Vì vậy, lợi ích của việc sử dụng phần mềm trung gian như Redux Thunk hoặc Redux Promise là các thành phần không nhận thức được cách thức người tạo hành động thực hiện và liệu họ có quan tâm đến trạng thái Redux hay không, liệu chúng có đồng bộ hay không đồng bộ hay không và họ có gọi người tạo hành động khác hay không . Nhược điểm là một chút thiếu quyết đoán, nhưng chúng tôi tin rằng nó đáng giá trong các ứng dụng thực tế.

Cuối cùng, Redux Thunk và bạn bè chỉ là một cách tiếp cận khả thi đối với các yêu cầu không đồng bộ trong các ứng dụng Redux. Một cách tiếp cận thú vị khác là Redux Saga cho phép bạn xác định các trình tiện ích chạy dài (Thời gian sagas ') thực hiện các hành động khi chúng đến và chuyển đổi hoặc thực hiện các yêu cầu trước khi đưa ra các hành động. Điều này chuyển logic từ những người sáng tạo hành động thành sagas. Bạn có thể muốn kiểm tra nó, và sau đó chọn những gì phù hợp với bạn nhất.

Tôi đã tìm kiếm repo Redux để tìm manh mối và thấy rằng Action Creators được yêu cầu phải là các hàm thuần túy trong quá khứ.

Điều này là không chính xác. Các tài liệu đã nói điều này, nhưng các tài liệu đã sai.
Người tạo hành động không bao giờ được yêu cầu là chức năng thuần túy.
Chúng tôi đã sửa các tài liệu để phản ánh điều đó.


57
Có thể cách ngắn gọn để nói suy nghĩ của Dan là: phần mềm trung gian là cách tiếp cận tập trung, cách này cho phép bạn giữ các thành phần của mình đơn giản hơn và tổng quát hóa và kiểm soát luồng dữ liệu ở một nơi. Nếu bạn duy trì ứng dụng lớn, bạn sẽ thích nó =)
Sergey Lapin

3
@asdfasdfads Tôi không hiểu tại sao nó không hoạt động. Nó sẽ hoạt động chính xác như nhau; đặt alertsau khi dispatch()ing hành động.
Dan Abramov

9
Dòng áp chót trong ví dụ mã đầu tiên của bạn : loadData(this.props.dispatch, this.props.userId); // don't forget to pass dispatch. Tại sao tôi cần phải vượt qua trong công văn? Nếu theo quy ước, chỉ có một cửa hàng toàn cầu duy nhất, tại sao tôi không tham khảo trực tiếp và làm store.dispatchbất cứ khi nào tôi cần, ví dụ, trong loadData?
Søren Debois

10
@ SørenDebois Nếu ứng dụng của bạn chỉ là phía máy khách sẽ hoạt động. Nếu nó được hiển thị trên máy chủ, bạn sẽ muốn có một phiên bản khác nhau storecho mỗi yêu cầu để bạn không thể xác định trước.
Dan Abramov

3
Chỉ muốn chỉ ra rằng câu trả lời này có 139 dòng, gấp 9,92 lần so với mã nguồn của redux-thunk, bao gồm 14 dòng: github.com/gaearon/redux-thunk/blob/master/src/index.js
Chàng trai

447

Bạn không.

Nhưng ... bạn nên sử dụng redux-saga :)

Câu trả lời của Dan Abramov là đúng redux-thunknhưng tôi sẽ nói thêm một chút về redux-saga khá giống nhưng mạnh hơn.

Khai báo VS bắt buộc

  • DOM : jQuery là bắt buộc / React là khai báo
  • Monads : IO là mệnh lệnh / Miễn phí là khai báo
  • Hiệu ứng Redux : redux-thunklà mệnh lệnh / redux-sagalà khai báo

Khi bạn có một cái rương trong tay, như một đơn vị IO hoặc một lời hứa, bạn không thể dễ dàng biết nó sẽ làm gì khi bạn thực hiện. Cách duy nhất để kiểm tra một thunk là thực thi nó và chế nhạo người điều phối (hoặc toàn bộ thế giới bên ngoài nếu nó tương tác với nhiều thứ hơn ...).

Nếu bạn đang sử dụng giả, thì bạn không làm lập trình chức năng.

Nhìn qua lăng kính của các hiệu ứng phụ, giả là một lá cờ cho thấy mã của bạn không trong sạch, và trong mắt của lập trình viên chức năng, bằng chứng rằng có gì đó không đúng. Thay vì tải xuống một thư viện để giúp chúng tôi kiểm tra tảng băng còn nguyên vẹn, chúng ta nên đi thuyền xung quanh nó. Một anh chàng TDD / Java khó tính đã từng hỏi tôi làm thế nào để bạn chế giễu trong Clojure. Câu trả lời là, chúng ta thường không. Chúng ta thường xem đó là một dấu hiệu chúng ta cần cấu trúc lại mã của mình.

Nguồn

Các sagas (như chúng đã được triển khai redux-saga) là khai báo và giống như các thành phần Free monad hoặc React, chúng dễ dàng hơn để kiểm tra mà không cần giả.

Xem thêm bài viết này :

trong FP hiện đại, chúng ta không nên viết chương trình - chúng ta nên viết mô tả về các chương trình, sau đó chúng ta có thể hướng nội, biến đổi và diễn giải theo ý muốn.

(Trên thực tế, Redux-saga giống như một phép lai: dòng chảy là bắt buộc nhưng hiệu ứng là khai báo)

Nhầm lẫn: hành động / sự kiện / lệnh ...

Có rất nhiều sự nhầm lẫn trong thế giới frontend về cách một số khái niệm phụ trợ như CQRS / EventSource và Flux / Redux có thể liên quan với nhau, chủ yếu là vì trong Flux chúng ta sử dụng thuật ngữ "hành động" đôi khi có thể đại diện cho cả mã mệnh lệnh ( LOAD_USER) và sự kiện ( USER_LOADED). Tôi tin rằng giống như tìm nguồn cung ứng sự kiện, bạn chỉ nên gửi các sự kiện.

Sử dụng sagas trong thực tế

Hãy tưởng tượng một ứng dụng có liên kết đến hồ sơ người dùng. Cách thức thành ngữ để xử lý việc này với mỗi phần mềm trung gian sẽ là:

redux-thunk

<div onClick={e => dispatch(actions.loadUserProfile(123)}>Robert</div>

function loadUserProfile(userId) {
  return dispatch => fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'USER_PROFILE_LOADED', data }),
      err => dispatch({ type: 'USER_PROFILE_LOAD_FAILED', err })
    );
}

redux-saga

<div onClick={e => dispatch({ type: 'USER_NAME_CLICKED', payload: 123 })}>Robert</div>


function* loadUserProfileOnNameClick() {
  yield* takeLatest("USER_NAME_CLICKED", fetchUser);
}

function* fetchUser(action) {
  try {
    const userProfile = yield fetch(`http://data.com/${action.payload.userId }`)
    yield put({ type: 'USER_PROFILE_LOADED', userProfile })
  } 
  catch(err) {
    yield put({ type: 'USER_PROFILE_LOAD_FAILED', err })
  }
}

Câu chuyện này dịch là:

mỗi khi tên người dùng được nhấp, hãy tìm nạp hồ sơ người dùng và sau đó gửi một sự kiện với hồ sơ được tải.

Như bạn có thể thấy, có một số lợi thế của redux-saga.

Việc sử dụng takeLatestgiấy phép để thể hiện rằng bạn chỉ quan tâm để có được dữ liệu của tên người dùng cuối cùng được nhấp (xử lý các vấn đề tương tranh trong trường hợp người dùng nhấp rất nhanh vào nhiều tên người dùng). Loại công cụ này là khó khăn với thunks. Bạn có thể đã sử dụng takeEverynếu bạn không muốn hành vi này.

Bạn giữ người sáng tạo hành động tinh khiết. Lưu ý rằng vẫn hữu ích để giữ các ActionCreators (trong sagas putvà các thành phần dispatch), vì nó có thể giúp bạn thêm xác thực hành động (xác nhận / luồng / bản thảo) trong tương lai.

Mã của bạn trở nên dễ kiểm tra hơn nhiều vì các hiệu ứng là khai báo

Bạn không cần phải kích hoạt các cuộc gọi giống như rpc nữa actions.loadUser(). Giao diện người dùng của bạn chỉ cần gửi những gì ĐÃ XẢY RA. Chúng tôi chỉ bắn các sự kiện (luôn luôn ở thì quá khứ!) Và không hành động nữa. Điều này có nghĩa là bạn có thể tạo ra các "con vịt" tách rời hoặc Bối cảnh bị ràng buộc và câu chuyện có thể đóng vai trò là điểm khớp nối giữa các thành phần mô-đun này.

Điều này có nghĩa là các chế độ xem của bạn dễ quản lý hơn vì chúng không cần phải chứa lớp dịch đó giữa những gì đã xảy ra và những gì sẽ xảy ra như một hiệu ứng

Ví dụ, hãy tưởng tượng một chế độ xem cuộn vô hạn. CONTAINER_SCROLLEDcó thể dẫn đến NEXT_PAGE_LOADED, nhưng nó có thực sự là trách nhiệm của container có thể cuộn để quyết định xem chúng ta có nên tải trang khác hay không? Sau đó, anh ta phải nhận thức được những thứ phức tạp hơn như liệu trang cuối cùng đã được tải thành công hay chưa, nếu đã có một trang cố gắng tải, hoặc nếu không còn mục nào để tải nữa? Tôi không nghĩ vậy: để có thể sử dụng lại tối đa, thùng chứa có thể cuộn chỉ cần mô tả rằng nó đã được cuộn. Việc tải trang là "hiệu ứng kinh doanh" của cuộn đó

Một số người có thể lập luận rằng các máy phát điện vốn có thể ẩn trạng thái bên ngoài cửa hàng redux với các biến cục bộ, nhưng nếu bạn bắt đầu sắp xếp những thứ phức tạp bên trong thunks bằng cách bắt đầu bộ hẹn giờ, v.v ... bạn sẽ gặp vấn đề tương tự. Và có một selecthiệu ứng mà bây giờ cho phép để có được một số trạng thái từ cửa hàng Redux của bạn.

Sagas có thể được du hành thời gian và cũng cho phép ghi nhật ký dòng chảy và các công cụ phát triển phức tạp hiện đang được thực hiện. Dưới đây là một số ghi nhật ký luồng không đồng bộ đơn giản đã được triển khai:

khai thác dòng chảy saga

Tách

Sagas không chỉ thay thế redux thunks. Họ đến từ phụ trợ / hệ thống phân tán / tìm nguồn cung ứng sự kiện.

Đó là một quan niệm sai lầm rất phổ biến rằng sagas chỉ ở đây để thay thế đàn hồi của bạn với khả năng kiểm tra tốt hơn. Trên thực tế đây chỉ là một chi tiết thực hiện của redux-saga. Sử dụng hiệu ứng khai báo tốt hơn thunks cho khả năng kiểm tra, nhưng mẫu saga có thể được triển khai trên đầu trang của mã bắt buộc hoặc khai báo.

Ở nơi đầu tiên, saga là một phần mềm cho phép điều phối các giao dịch chạy dài (tính nhất quán cuối cùng) và các giao dịch trên các bối cảnh bị ràng buộc khác nhau (biệt ngữ thiết kế hướng tên miền).

Để đơn giản hóa điều này cho thế giới frontend, hãy tưởng tượng có widget1 và widget2. Khi một số nút trên widget1 được nhấp, thì nó sẽ có hiệu lực trên widget2. Thay vì ghép 2 widget lại với nhau (ví dụ widget1 gửi một hành động nhắm vào widget2), widget1 chỉ gửi rằng nút của nó đã được bấm. Sau đó, saga lắng nghe nút này bấm và sau đó cập nhật widget2 bằng cách phân tán một sự kiện mới mà widget2 biết.

Điều này thêm một mức độ gián tiếp không cần thiết cho các ứng dụng đơn giản, nhưng làm cho nó dễ dàng hơn để mở rộng các ứng dụng phức tạp. Bây giờ bạn có thể xuất bản widget1 và widget2 lên các kho npm khác nhau để họ không bao giờ phải biết về nhau, mà không cần phải chia sẻ sổ đăng ký hành động toàn cầu. Hai widget hiện đang có bối cảnh giới hạn có thể sống riêng. Họ không cần nhau nhất quán và cũng có thể được sử dụng lại trong các ứng dụng khác. Câu chuyện là điểm kết nối giữa hai vật dụng phối hợp chúng theo cách có ý nghĩa cho doanh nghiệp của bạn.

Một số bài viết hay về cách cấu trúc ứng dụng Redux của bạn, trên đó bạn có thể sử dụng Redux-saga vì lý do tách rời:

Một usecase cụ thể: hệ thống thông báo

Tôi muốn các thành phần của mình có thể kích hoạt hiển thị thông báo trong ứng dụng. Nhưng tôi không muốn các thành phần của mình được kết hợp chặt chẽ với hệ thống thông báo có quy tắc kinh doanh riêng (tối đa 3 thông báo được hiển thị cùng lúc, xếp hàng thông báo, thời gian hiển thị 4 giây, v.v.).

Tôi không muốn các thành phần JSX của mình quyết định khi nào thông báo sẽ hiển thị / ẩn. Tôi chỉ cung cấp cho nó khả năng yêu cầu thông báo và để lại các quy tắc phức tạp bên trong câu chuyện. Loại công cụ này khá khó thực hiện với thunks hoặc lời hứa.

thông báo

Tôi đã mô tả ở đây làm thế nào điều này có thể được thực hiện với saga

Tại sao nó được gọi là Saga?

Thuật ngữ saga xuất phát từ thế giới phụ trợ. Ban đầu tôi đã giới thiệu Yassine (tác giả của Redux-saga) cho thuật ngữ đó trong một cuộc thảo luận dài .

Ban đầu, thuật ngữ đó được giới thiệu bằng một tờ giấy , mẫu saga được cho là được sử dụng để xử lý tính nhất quán cuối cùng trong các giao dịch phân tán, nhưng việc sử dụng nó đã được các nhà phát triển phụ trợ mở rộng sang định nghĩa rộng hơn để giờ đây nó cũng bao trùm cả "trình quản lý quy trình" mẫu (bằng cách nào đó mẫu saga gốc là một hình thức quản lý quy trình chuyên biệt).

Ngày nay, thuật ngữ "saga" gây nhầm lẫn vì nó có thể mô tả 2 điều khác nhau. Vì nó được sử dụng trong redux-saga, nó không mô tả cách xử lý các giao dịch phân tán mà là cách phối hợp các hành động trong ứng dụng của bạn. redux-sagacũng có thể được gọi redux-process-manager.

Xem thêm:

Lựa chọn thay thế

Nếu bạn không thích ý tưởng sử dụng máy phát điện nhưng bạn quan tâm đến mẫu saga và các thuộc tính tách rời của nó, bạn cũng có thể đạt được điều tương tự với redux-epic obsable sử dụng tên để mô tả chính xác mẫu tương tự, nhưng với RxJS. Nếu bạn đã quen thuộc với Rx, bạn sẽ cảm thấy như ở nhà.

const loadUserProfileOnNameClickEpic = action$ =>
  action$.ofType('USER_NAME_CLICKED')
    .switchMap(action =>
      Observable.ajax(`http://data.com/${action.payload.userId}`)
        .map(userProfile => ({
          type: 'USER_PROFILE_LOADED',
          userProfile
        }))
        .catch(err => Observable.of({
          type: 'USER_PROFILE_LOAD_FAILED',
          err
        }))
    );

Một số tài nguyên hữu ích redux-saga

Tư vấn năm 2017

  • Đừng lạm dụng Redux-saga chỉ vì lợi ích của việc sử dụng nó. Các lệnh gọi API có thể kiểm tra chỉ không có giá trị.
  • Đừng loại bỏ thunks khỏi dự án của bạn cho hầu hết các trường hợp đơn giản.
  • Đừng ngần ngại gửi thunks yield put(someActionThunk)nếu nó có ý nghĩa.

Nếu bạn sợ sử dụng Redux-saga (hoặc có thể quan sát được Redux) nhưng chỉ cần mẫu tách rời, hãy kiểm tra đăng ký redux-gửi-đăng ký : nó cho phép nghe công văn và kích hoạt công văn mới trong trình nghe.

const unsubscribe = store.addDispatchListener(action => {
  if (action.type === 'ping') {
    store.dispatch({ type: 'pong' });
  }
});

64
Điều này đang trở nên tốt hơn mỗi lần tôi xem lại. Hãy xem xét biến nó thành một bài viết trên blog :).
RainerAtSprite

4
Cảm ơn bạn đã viết tốt lên. Tuy nhiên tôi không đồng ý về các khía cạnh nhất định. LOAD_USER bắt buộc như thế nào? Đối với tôi, nó không chỉ là khai báo - nó còn mang lại mã dễ đọc. Ví dụ như. "Khi tôi nhấn nút này, tôi muốn ADD_ITEM". Tôi có thể nhìn vào mã và hiểu chính xác những gì đang xảy ra. Nếu nó được gọi là một cái gì đó có hiệu lực của "BUTTON_CLICK", tôi sẽ phải tìm kiếm nó.
swelet

4
Câu trả lời của NIce. Bây giờ có một lựa chọn khác: github.com/bledh/redux-observable
swennemen

4
@swelet xin lỗi vì anwer muộn. Khi bạn gửi đi ADD_ITEM, điều bắt buộc là vì bạn gửi một hành động nhằm mục đích có ảnh hưởng đến cửa hàng của bạn: bạn mong đợi hành động đó sẽ làm một cái gì đó. Việc tuyên bố bao trùm triết lý tìm nguồn cung ứng sự kiện: bạn không gửi các hành động để kích hoạt các thay đổi trên ứng dụng của mình, nhưng bạn gửi các sự kiện trong quá khứ để mô tả những gì đã xảy ra trong ứng dụng của bạn. Công văn của một sự kiện phải đủ để xem xét rằng trạng thái của ứng dụng đã thay đổi. Thực tế là có một cửa hàng Redux phản ứng với sự kiện là một chi tiết triển khai tùy chọn
Sebastien Lorber

3
Tôi không thích câu trả lời này vì nó làm mất tập trung vào câu hỏi thực tế để tiếp thị thư viện của ai đó. Câu trả lời này cung cấp một so sánh của hai thư viện, đó không phải là mục đích của câu hỏi. Câu hỏi thực tế là hỏi liệu có nên sử dụng phần mềm trung gian hay không, điều này được giải thích bằng câu trả lời được chấp nhận.
Abhinav Manchanda

31

Câu trả lời ngắn : có vẻ như là một cách tiếp cận hoàn toàn hợp lý cho vấn đề không đồng bộ với tôi. Với một vài hãy cẩn thận.

Tôi đã có một dòng suy nghĩ rất giống nhau khi làm việc trong một dự án mới, chúng tôi mới bắt đầu công việc của mình. Tôi là một fan hâm mộ lớn của hệ thống thanh lịch của vanilla Redux khi cập nhật cửa hàng và đăng ký lại các thành phần theo cách không nằm ngoài sự can đảm của cây thành phần React. Nó có vẻ kỳ lạ đối với tôi để móc vào dispatchcơ chế thanh lịch đó để xử lý sự không đồng bộ.

Cuối cùng tôi đã đi với một cách tiếp cận thực sự tương tự với những gì bạn có trong một thư viện mà tôi đã tìm ra trong dự án của chúng tôi, mà chúng tôi gọi là bộ điều khiển phản ứng-redux-control .

Tôi đã kết thúc không đi theo cách tiếp cận chính xác mà bạn có ở trên vì một vài lý do:

  1. Cách bạn viết, những chức năng điều phối đó không có quyền truy cập vào cửa hàng. Bạn có thể phần nào khắc phục điều đó bằng cách yêu cầu các thành phần UI của bạn vượt qua tất cả các thông tin mà chức năng gửi đi cần. Nhưng tôi cho rằng điều này kết hợp các thành phần UI đó với logic điều phối không cần thiết. Và có vấn đề hơn, không có cách rõ ràng nào cho chức năng điều phối truy cập trạng thái được cập nhật trong các phần tiếp theo không đồng bộ.
  2. Các chức năng điều phối có quyền truy cập vào dispatchchính nó thông qua phạm vi từ vựng. Điều này giới hạn các tùy chọn để tái cấu trúc một khi connectcâu lệnh đó vượt quá tầm kiểm soát - và nó trông khá khó sử dụng chỉ với một updatephương thức đó. Vì vậy, bạn cần một số hệ thống để cho phép bạn soạn các hàm điều phối đó nếu bạn chia chúng thành các mô-đun riêng biệt.

Kết hợp lại với nhau, bạn phải dựng lên một số hệ thống để cho phép dispatchvà cửa hàng được đưa vào các chức năng điều phối của bạn, cùng với các tham số của sự kiện. Tôi biết ba cách tiếp cận hợp lý cho việc tiêm phụ thuộc này:

  • redux-thunk thực hiện điều này theo một cách có chức năng, bằng cách chuyển chúng vào thunks của bạn (làm cho chúng không hoàn toàn chính xác, theo định nghĩa mái vòm). Tôi đã không làm việc với các dispatchphương pháp tiếp cận phần mềm trung gian khác , nhưng tôi cho rằng về cơ bản chúng giống nhau.
  • Reac-redux-controller thực hiện điều này với coroutine. Như một phần thưởng, nó cũng cung cấp cho bạn quyền truy cập vào "bộ chọn", đây là các chức năng bạn có thể đã chuyển qua làm đối số đầu tiên connect, thay vì phải làm việc trực tiếp với cửa hàng thô, chuẩn hóa.
  • Bạn cũng có thể thực hiện theo cách hướng đối tượng bằng cách đưa chúng vào thisbối cảnh, thông qua nhiều cơ chế có thể.

Cập nhật

Nó xảy ra với tôi rằng một phần của câu hỏi hóc búa này là một hạn chế của phản ứng-redux . Đối số đầu tiên để connectcó một ảnh chụp nhanh trạng thái, nhưng không gửi đi. Đối số thứ hai được gửi đi nhưng không phải là nhà nước. Không đối số nào có được một thunk đóng trên trạng thái hiện tại, vì có thể thấy trạng thái được cập nhật tại thời điểm tiếp tục / gọi lại.


22

Mục tiêu của Abramov - và lý tưởng của mọi người - chỉ đơn giản là gói gọn sự phức tạp (và các cuộc gọi không đồng bộ) ở nơi phù hợp nhất .

Đâu là nơi tốt nhất để làm điều đó trong dataflow tiêu chuẩn? Làm thế nào về:

  • Giảm ? Không đời nào. Chúng phải là các chức năng thuần túy không có tác dụng phụ. Cập nhật cửa hàng là nghiêm trọng, kinh doanh phức tạp. Đừng làm ô nhiễm nó.
  • Thành phần câm xem? Chắc chắn là không. Họ có một mối quan tâm: trình bày và tương tác với người dùng, và nên đơn giản nhất có thể.
  • Thành phần container? Có thể, nhưng tối ưu phụ. Điều có ý nghĩa ở chỗ container là nơi chúng tôi gói gọn một số phức tạp liên quan đến chế độ xem và tương tác với cửa hàng, nhưng:
    • Các container cần phải phức tạp hơn các thành phần câm, nhưng đó vẫn là một trách nhiệm duy nhất: cung cấp các ràng buộc giữa chế độ xem và trạng thái / cửa hàng. Logic async của bạn là một mối quan tâm hoàn toàn riêng biệt từ đó.
    • Bằng cách đặt nó vào một thùng chứa, bạn sẽ khóa logic async của mình vào một ngữ cảnh duy nhất, cho một chế độ xem / tuyến đường duy nhất. Ý kiến ​​tồi. Lý tưởng nhất là tất cả có thể tái sử dụng, và hoàn toàn tách rời.
  • Một mô-đun dịch vụ khác? Ý tưởng tồi: bạn cần tiêm quyền truy cập vào cửa hàng, đó là một cơn ác mộng về khả năng duy trì / kiểm tra. Tốt hơn là đi với hạt Redux và chỉ truy cập vào cửa hàng bằng cách sử dụng API / mô hình được cung cấp.
  • Hành động và các phần mềm giải thích chúng? Tại sao không?! Để bắt đầu, đó là lựa chọn chính duy nhất chúng tôi còn lại. :-) Về mặt logic hơn, hệ thống hành động được tách rời logic thực thi mà bạn có thể sử dụng từ mọi nơi. Nó có quyền truy cập vào cửa hàng và có thể gửi thêm hành động. Nó có một trách nhiệm duy nhất là tổ chức luồng điều khiển và dữ liệu xung quanh ứng dụng và hầu hết async phù hợp với điều đó.
    • Còn những người sáng tạo hành động thì sao? Tại sao không chỉ làm async trong đó, thay vì trong chính các hành động và trong Middleware?
      • Đầu tiên và quan trọng nhất, những người sáng tạo không có quyền truy cập vào cửa hàng, như phần mềm trung gian. Điều đó có nghĩa là bạn không thể gửi các hành động dự phòng mới, không thể đọc từ cửa hàng để soạn async của bạn, v.v.
      • Vì vậy, hãy giữ sự phức tạp ở một nơi phức tạp cần thiết và giữ mọi thứ khác đơn giản. Các trình tạo sau đó có thể là các hàm đơn giản, tương đối thuần túy, dễ kiểm tra.

Thành phần container - tại sao không? Do các thành phần đóng vai trò trong React, một container có thể đóng vai trò là lớp dịch vụ và nó đã có một cửa hàng thông qua DI (đạo cụ). Bằng cách đặt nó vào một thùng chứa, bạn sẽ khóa logic async của mình vào một ngữ cảnh duy nhất, cho một chế độ xem / tuyến đường duy nhất - làm thế nào? Một thành phần có thể có nhiều trường hợp. Nó có thể được tách rời khỏi bản trình bày, ví dụ như với prop prop. Tôi đoán câu trả lời có thể có lợi nhiều hơn từ các ví dụ ngắn chứng minh luận điểm.
Bình Estus

Tôi thích câu trả lời này!
Mauricio Avendaño

13

Để trả lời câu hỏi được hỏi lúc đầu:

Tại sao thành phần container không thể gọi API async, sau đó gửi các hành động?

Hãy nhớ rằng những tài liệu đó là dành cho Redux, không phải Redux cộng với React. Các cửa hàng Redux được nối với các thành phần React có thể thực hiện chính xác những gì bạn nói, nhưng một cửa hàng Plain Jane Redux không có phần mềm trung gian không chấp nhận các đối số dispatchngoại trừ các đối tượng đơn giản.

Không có phần mềm trung gian, tất nhiên bạn vẫn có thể làm

const store = createStore(reducer);
MyAPI.doThing().then(resp => store.dispatch(...));

Nhưng đó là một trường hợp tương tự trong đó sự không đồng bộ được bao bọc xung quanh Redux chứ không phải do Redux xử lý . Vì vậy, phần mềm trung gian cho phép không đồng bộ bằng cách sửa đổi những gì có thể được truyền trực tiếp vào dispatch.


Điều đó nói rằng, tinh thần đề nghị của bạn là, tôi nghĩ, hợp lệ. Chắc chắn có nhiều cách khác bạn có thể xử lý sự không đồng bộ trong ứng dụng Redux + React.

Một lợi ích của việc sử dụng phần mềm trung gian là bạn có thể tiếp tục sử dụng các trình tạo hành động như bình thường mà không phải lo lắng về cách chính xác chúng được nối. Ví dụ, bằng cách sử dụng redux-thunk, mã bạn đã viết sẽ trông rất giống

function updateThing() {
  return dispatch => {
    dispatch({
      type: ActionTypes.STARTED_UPDATING
    });
    AsyncApi.getFieldValue()
      .then(result => dispatch({
        type: ActionTypes.UPDATED,
        payload: result
      }));
  }
}

const ConnectedApp = connect(
  (state) => { ...state },
  { update: updateThing }
)(App);

trông không khác gì so với bản gốc - nó chỉ bị xáo trộn một chút - và connectkhông biết đó updateThinglà (hoặc cần phải) không đồng bộ.

Nếu bạn cũng muốn hỗ trợ các lời hứa , quan sát , sagas hoặc người tạo hành động tùy chỉnhtuyên bố điên rồ , thì Redux có thể thực hiện điều đó chỉ bằng cách thay đổi những gì bạn chuyển sang dispatch(còn gọi là những gì bạn trả lại từ người tạo hành động). Không cần mucking với các thành phần React (hoặc connectcuộc gọi) cần thiết.


Bạn khuyên nên gửi một sự kiện khác về hoàn thành hành động. Điều đó sẽ không hoạt động khi bạn cần hiển thị cảnh báo () sau khi hoàn thành hành động. Hứa hẹn bên trong các thành phần React làm việc mặc dù. Tôi hiện đang đề xuất phương pháp Hứa.
catamphetamine

8

OK, chúng ta hãy bắt đầu xem phần mềm trung gian hoạt động như thế nào trước, câu trả lời khá rõ, đây là mã nguồn một hàm pplyMiddleWare trong Redux:

function applyMiddleware() {
  for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }

  return function (createStore) {
    return function (reducer, preloadedState, enhancer) {
      var store = createStore(reducer, preloadedState, enhancer);
      var _dispatch = store.dispatch;
      var chain = [];

      var middlewareAPI = {
        getState: store.getState,
        dispatch: function dispatch(action) {
          return _dispatch(action);
        }
      };
      chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);
      });
      _dispatch = compose.apply(undefined, chain)(store.dispatch);

      return _extends({}, store, {
        dispatch: _dispatch
      });
    };
  };
}

Nhìn vào phần này, xem công văn của chúng tôi trở thành một chức năng như thế nào .

  ...
  getState: store.getState,
  dispatch: function dispatch(action) {
  return _dispatch(action);
}
  • Lưu ý rằng mỗi phần mềm trung gian sẽ được cung cấp dispatchvà các getStatehàm như các đối số được đặt tên.

OK, đây là cách Redux-thunk là một trong những phần mềm trung gian được sử dụng nhiều nhất cho Redux tự giới thiệu:

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 phương thức lưu trữ và getState làm tham số.

Vì vậy, như bạn thấy, nó sẽ trả về một hàm thay vì một hành động, có nghĩa là bạn có thể đợi và gọi nó bất cứ lúc nào bạn muốn vì đó là một chức năng ...

Vì vậy, cái quái gì là thunk? Đó là cách nó được giới thiệu trong Wikipedia:

Trong lập trình máy tính, một thunk là một chương trình con được sử dụng để tiêm một phép tính bổ sung vào một chương trình con khác. Thunk chủ yếu được sử dụng để trì hoãn một phép tính cho đến khi cần, hoặc để chèn các hoạt động ở đầu hoặc cuối của chương trình con khác. Chúng có nhiều ứng dụng khác để tạo mã trình biên dịch và trong lập trình mô-đun.

Thuật ngữ này có nguồn gốc là một từ phái sinh của "think".

Thunk là một hàm bao bọc một biểu thức để trì hoãn việc đánh giá nó.

//calculation of 1 + 2 is immediate 
//x === 3 
let x = 1 + 2;

//calculation of 1 + 2 is delayed 
//foo can be called later to perform the calculation 
//foo is a thunk! 
let foo = () => 1 + 2;

Vì vậy, hãy xem khái niệm này dễ dàng như thế nào và nó có thể giúp bạn quản lý các hành động không đồng bộ của mình như thế nào ...

Đó là thứ bạn có thể sống mà không cần nó, nhưng hãy nhớ trong lập trình luôn có những cách tốt hơn, gọn gàng hơn và phù hợp hơn để làm mọi việc ...

Áp dụng phần mềm trung gian Redux


1
Lần đầu tiên trên SO, không đọc gì cả. Nhưng chỉ thích bài viết nhìn chằm chằm vào hình ảnh. Tuyệt vời, gợi ý, và nhắc nhở.
Bhojendra Rauniyar

2

Để sử dụng Redux-saga là phần mềm trung gian tốt nhất trong triển khai React-redux.

Ví dụ: store.js

  import createSagaMiddleware from 'redux-saga';
  import { createStore, applyMiddleware } from 'redux';
  import allReducer from '../reducer/allReducer';
  import rootSaga from '../saga';

  const sagaMiddleware = createSagaMiddleware();
  const store = createStore(
     allReducer,
     applyMiddleware(sagaMiddleware)
   )

   sagaMiddleware.run(rootSaga);

 export default store;

Và sau đó là saga.js

import {takeLatest,delay} from 'redux-saga';
import {call, put, take, select} from 'redux-saga/effects';
import { push } from 'react-router-redux';
import data from './data.json';

export function* updateLesson(){
   try{
       yield put({type:'INITIAL_DATA',payload:data}) // initial data from json
       yield* takeLatest('UPDATE_DETAIL',updateDetail) // listen to your action.js 
   }
   catch(e){
      console.log("error",e)
     }
  }

export function* updateDetail(action) {
  try{
       //To write store update details
   }  
    catch(e){
       console.log("error",e)
    } 
 }

export default function* rootSaga(){
    yield [
        updateLesson()
       ]
    }

Và sau đó là Action.js

 export default function updateFruit(props,fruit) {
    return (
       {
         type:"UPDATE_DETAIL",
         payload:fruit,
         props:props
       }
     )
  }

Và sau đó là lesser.js

import {combineReducers} from 'redux';

const fetchInitialData = (state=[],action) => {
    switch(action.type){
      case "INITIAL_DATA":
          return ({type:action.type, payload:action.payload});
          break;
      }
     return state;
  }
 const updateDetailsData = (state=[],action) => {
    switch(action.type){
      case "INITIAL_DATA":
          return ({type:action.type, payload:action.payload});
          break;
      }
     return state;
  }
const allReducers =combineReducers({
   data:fetchInitialData,
   updateDetailsData
 })
export default allReducers; 

Và sau đó là main.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './app/components/App.jsx';
import {Provider} from 'react-redux';
import store from './app/store';
import createRoutes from './app/routes';

const initialState = {};
const store = configureStore(initialState, browserHistory);

ReactDOM.render(
       <Provider store={store}>
          <App />  /*is your Component*/
       </Provider>, 
document.getElementById('app'));

thử cái này đi


3
Đây là nội dung nghiêm trọng đối với người chỉ muốn gọi điểm cuối API để trả về thực thể hoặc danh sách thực thể. Bạn khuyên bạn, "cứ làm cái này ... rồi cái này, rồi cái này, cái kia, rồi cái kia, rồi những thứ khác, rồi tiếp tục, rồi làm ..". Nhưng bạn ơi, đây là TRƯỚC, chúng ta chỉ cần gọi BACKEND để cung cấp cho chúng tôi dữ liệu sẵn sàng để được sử dụng trên frontend. Nếu đây là con đường để đi, một cái gì đó sai, một cái gì đó thực sự sai và ngày nay ai đó không áp dụng KISS
zameb

Xin chào, Sử dụng khối thử và bắt cho Cuộc gọi API. Khi API đã đưa ra phản hồi, hãy gọi các loại hành động Reducer.
SM Chinna

1
@zameb Bạn có thể đúng, nhưng sau đó, khiếu nại của bạn là với chính Redux và tất cả những gì đã nghe được trong khi cố gắng giảm độ phức tạp.
jorisw

1

Có những người tạo hành động đồng bộ và sau đó có những người tạo hành động không đồng bộ.

Trình tạo hành động đồng bộ là một trình tạo mà khi chúng ta gọi nó, nó sẽ trả về ngay một đối tượng Hành động với tất cả dữ liệu có liên quan đến đối tượng đó và nó sẵn sàng được xử lý bởi các bộ giảm tốc của chúng ta.

Người tạo hành động không đồng bộ là một trong đó nó sẽ cần một ít thời gian trước khi nó sẵn sàng để gửi một hành động.

Theo định nghĩa, bất cứ khi nào bạn có một người tạo hành động thực hiện yêu cầu mạng, nó sẽ luôn đủ điều kiện là người tạo hành động không đồng bộ.

Nếu bạn muốn có các trình tạo hành động không đồng bộ bên trong ứng dụng Redux, bạn phải cài đặt một thứ gọi là phần mềm trung gian sẽ cho phép bạn xử lý các trình tạo hành động không đồng bộ đó.

Bạn có thể xác minh điều này trong thông báo lỗi cho chúng tôi biết sử dụng phần mềm trung gian tùy chỉnh cho các hành động không đồng bộ.

Vậy phần mềm trung gian là gì và tại sao chúng ta cần nó cho luồng không đồng bộ trong Redux?

Trong bối cảnh các phần mềm trung gian redux như redux-thunk, một phần mềm trung gian giúp chúng ta đối phó với những người tạo hành động không đồng bộ vì đó là điều mà Redux không thể xử lý được.

Với một phần mềm trung gian được tích hợp vào chu trình Redux, chúng tôi vẫn đang gọi những người tạo hành động, điều đó sẽ trả lại một hành động sẽ được gửi đi nhưng bây giờ khi chúng tôi gửi một hành động, thay vì gửi trực tiếp tới tất cả các bộ giảm tốc của chúng tôi, chúng tôi sẽ để nói rằng một hành động sẽ được gửi qua tất cả các phần mềm trung gian khác nhau bên trong ứng dụng.

Bên trong một ứng dụng Redux duy nhất, chúng ta có thể có nhiều hoặc ít phần mềm trung gian như chúng ta muốn. Đối với hầu hết các phần, trong các dự án chúng tôi làm việc, chúng tôi sẽ có một hoặc hai phần mềm trung gian được nối với cửa hàng Redux của chúng tôi.

Phần mềm trung gian là một hàm JavaScript đơn giản sẽ được gọi với mỗi hành động mà chúng tôi gửi đi. Bên trong chức năng đó, một phần mềm trung gian có cơ hội ngăn chặn một hành động được gửi đến bất kỳ bộ giảm tốc nào, nó có thể sửa đổi một hành động hoặc chỉ gây rối với một hành động theo bất kỳ cách nào, ví dụ như chúng ta có thể tạo ra một phần mềm trung gian điều khiển nhật ký mỗi hành động bạn gửi chỉ để xem niềm vui của bạn.

Có một số lượng lớn phần mềm trung gian nguồn mở mà bạn có thể cài đặt làm phụ thuộc vào dự án của mình.

Bạn không bị giới hạn chỉ sử dụng phần mềm trung gian nguồn mở hoặc cài đặt chúng làm phần phụ thuộc. Bạn có thể viết phần mềm trung gian tùy chỉnh của riêng bạn và sử dụng nó bên trong cửa hàng Redux của bạn.

Một trong những cách sử dụng phổ biến hơn của phần mềm trung gian (và nhận được câu trả lời của bạn) là để đối phó với người tạo hành động không đồng bộ, có lẽ phần mềm trung gian phổ biến nhất hiện có là redux-thunk và đó là về cách giúp bạn đối phó với người tạo hành động không đồng bộ.

Có nhiều loại phần mềm trung gian khác cũng giúp bạn xử lý các trình tạo hành động không đồng bộ.


1

Để trả lời câu hỏi:

Tại sao thành phần container không thể gọi API async, sau đó gửi các hành động?

Tôi sẽ nói vì ít nhất hai lý do:

Lý do đầu tiên là sự tách biệt các mối quan tâm, đó không phải là công việc của việc action creatorgọi apivà lấy lại dữ liệu, bạn phải chuyển hai đối số choaction creator function , action typevà và a payload.

Lý do thứ hai là vì redux store đang chờ một đối tượng đơn giản với loại hành động bắt buộc và tùy chọn a payload(nhưng ở đây bạn cũng phải vượt qua tải trọng).

Trình tạo hành động phải là một đối tượng đơn giản như dưới đây:

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

Và công việc của Redux-Thunk midlewaređể dispachekết quả của bạn api callcho phù hợp action.


0

Khi làm việc trong một dự án doanh nghiệp, có nhiều yêu cầu có sẵn trong kho trung gian, chẳng hạn như (saga) không có sẵn trong luồng không đồng bộ đơn giản, dưới đây là một số:

  • Chạy yêu cầu song song
  • Kéo các hành động trong tương lai mà không cần phải chờ đợi
  • Cuộc gọi không chặn Hiệu ứng cuộc đua, ví dụ đón đầu tiên
  • phản hồi để bắt đầu quá trình Sắp xếp các nhiệm vụ của bạn (lần đầu tiên trong cuộc gọi đầu tiên)
  • Sáng tác
  • Hủy bỏ nhiệm vụ Động tự động thực hiện nhiệm vụ.
  • Hỗ trợ đồng thời Chạy Saga bên ngoài phần mềm trung gian redux.
  • Sử dụng các kênh

Danh sách dài chỉ cần xem lại phần nâng cao trong tài liệu saga

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.