ข้อดี / ข้อเสียของการใช้ redux-saga กับเครื่องกำเนิด ES6 เทียบกับ redux-thunk กับ ES2017 async / รอ


488

มีจำนวนมากของการพูดคุยเกี่ยวกับเด็กล่าสุดในเมือง Redux คือตอนนี้Redux-วีรชน / Redux-เทพนิยาย มันใช้ฟังก์ชั่นเครื่องกำเนิดสำหรับการฟังการกระทำ

ก่อนที่ฉันจะพันหัวฉันอยากรู้ว่าข้อดี / ข้อเสียของการใช้redux-sagaแทนที่จะเป็นวิธีการด้านล่างที่ฉันใช้redux-thunkกับ async / รอ

ส่วนประกอบอาจมีลักษณะเช่นนี้ส่งการกระทำเหมือนปกติ

import { login } from 'redux/auth';

class LoginForm extends Component {

  onClick(e) {
    e.preventDefault();
    const { user, pass } = this.refs;
    this.props.dispatch(login(user.value, pass.value));
  }

  render() {
    return (<div>
        <input type="text" ref="user" />
        <input type="password" ref="pass" />
        <button onClick={::this.onClick}>Sign In</button>
    </div>);
  } 
}

export default connect((state) => ({}))(LoginForm);

จากนั้นการกระทำของฉันมีลักษณะเช่นนี้:

// auth.js

import request from 'axios';
import { loadUserData } from './user';

// define constants
// define initial state
// export default reducer

export const login = (user, pass) => async (dispatch) => {
    try {
        dispatch({ type: LOGIN_REQUEST });
        let { data } = await request.post('/login', { user, pass });
        await dispatch(loadUserData(data.uid));
        dispatch({ type: LOGIN_SUCCESS, data });
    } catch(error) {
        dispatch({ type: LOGIN_ERROR, error });
    }
}

// more actions...

// user.js

import request from 'axios';

// define constants
// define initial state
// export default reducer

export const loadUserData = (uid) => async (dispatch) => {
    try {
        dispatch({ type: USERDATA_REQUEST });
        let { data } = await request.get(`/users/${uid}`);
        dispatch({ type: USERDATA_SUCCESS, data });
    } catch(error) {
        dispatch({ type: USERDATA_ERROR, error });
    }
}

// more actions...

6
ดูคำตอบของฉันเปรียบเทียบ redux-thunk กับ redux-saga ที่นี่: stackoverflow.com/a/34623840/82609
Sebastien Lorber

22
คืออะไร::ก่อนที่คุณthis.onClickทำ?
Downhillski

37
@ZhenyangHua มันเป็นระยะสั้นมือสำหรับการผูกฟังก์ชั่นเพื่อวัตถุ ( this) this.onClick = this.onClick.bind(this)อาคา แบบฟอร์มที่ยาวกว่ามักจะแนะนำให้ทำในตัวสร้างเนื่องจากมือสั้นผูกเข้ากับการเรนเดอร์ทุกครั้ง
hampusohlsson

7
ฉันเห็น. ขอบคุณ! ฉันเห็นผู้ใช้bind()จำนวนมากใช้ส่งthisต่อฟังก์ชัน แต่ฉันเริ่มใช้งาน() => method()ได้ทันที
Downhillski

2
@ โฮซาร์ฉันใช้ redux & redux-saga ในการผลิตอยู่พักหนึ่ง แต่จริง ๆ แล้วย้ายไปที่ MobX หลังจากสองสามเดือนเพราะค่าใช้จ่ายน้อยลง
hampusohlsson

คำตอบ:


461

ใน redux-saga ค่าเทียบเท่าของตัวอย่างข้างต้นจะเป็น

export function* loginSaga() {
  while(true) {
    const { user, pass } = yield take(LOGIN_REQUEST)
    try {
      let { data } = yield call(request.post, '/login', { user, pass });
      yield fork(loadUserData, data.uid);
      yield put({ type: LOGIN_SUCCESS, data });
    } catch(error) {
      yield put({ type: LOGIN_ERROR, error });
    }  
  }
}

export function* loadUserData(uid) {
  try {
    yield put({ type: USERDATA_REQUEST });
    let { data } = yield call(request.get, `/users/${uid}`);
    yield put({ type: USERDATA_SUCCESS, data });
  } catch(error) {
    yield put({ type: USERDATA_ERROR, error });
  }
}

สิ่งแรกที่ต้องแจ้งให้ทราบล่วงหน้าคือการที่เรากำลังเรียกฟังก์ชัน API yield call(func, ...args)โดยใช้แบบฟอร์ม ไม่ได้ดำเนินการผลที่ออกมาก็แค่สร้างวัตถุธรรมดาเช่นcall {type: 'CALL', func, args}การดำเนินการถูกมอบหมายไปยังมิดเดิลแวร์ redux-saga ซึ่งดูแลการเรียกใช้ฟังก์ชันและกลับมาทำงานกับตัวกำเนิด

ข้อได้เปรียบหลักคือคุณสามารถทดสอบเครื่องกำเนิดภายนอก Redux โดยใช้การตรวจสอบความเท่าเทียมกันอย่างง่าย

const iterator = loginSaga()

assert.deepEqual(iterator.next().value, take(LOGIN_REQUEST))

// resume the generator with some dummy action
const mockAction = {user: '...', pass: '...'}
assert.deepEqual(
  iterator.next(mockAction).value, 
  call(request.post, '/login', mockAction)
)

// simulate an error result
const mockError = 'invalid user/password'
assert.deepEqual(
  iterator.throw(mockError).value, 
  put({ type: LOGIN_ERROR, error: mockError })
)

โปรดทราบว่าเรากำลังเยาะเย้ยผลการโทร api เพียงแค่ฉีดข้อมูลที่เยาะเย้ยลงในnextวิธีของตัววนซ้ำ ข้อมูลการเยาะเย้ยเป็นวิธีที่ง่ายกว่าฟังก์ชั่นการเยาะเย้ย

yield take(ACTION)สิ่งที่สองที่จะแจ้งให้ทราบล่วงหน้าคือการเรียกร้องให้ Thunks ถูกเรียกโดยผู้สร้างการกระทำในแต่ละการกระทำใหม่ (เช่นLOGIN_REQUEST) เช่นการกระทำจะถูกผลักไปที่ถังอย่างต่อเนื่องและถังไม่มีการควบคุมเมื่อหยุดการจัดการการกระทำเหล่านั้น

ใน redux-saga เครื่องปั่นไฟดึงการกระทำต่อไป เช่นพวกเขามีการควบคุมเวลาที่จะฟังการกระทำบางอย่างและเมื่อใดที่จะไม่ ในตัวอย่างข้างต้นคำแนะนำการไหลจะถูกวางไว้ภายในwhile(true)ลูปดังนั้นมันจะฟังการกระทำที่เข้ามาแต่ละครั้งซึ่งค่อนข้างเลียนแบบพฤติกรรมการผลักดันอันธพาล

วิธีการดึงให้ใช้การควบคุมการไหลที่ซับซ้อน สมมติว่าเราต้องการเพิ่มข้อกำหนดต่อไปนี้

  • จัดการกับการกระทำของผู้ใช้ LOGOUT

  • เมื่อลงชื่อเข้าใช้สำเร็จครั้งแรกเซิร์ฟเวอร์จะส่งคืนโทเค็นที่หมดอายุในการหน่วงเวลาบางอย่างที่เก็บในexpires_inฟิลด์ เราจะต้องรีเฟรชการอนุญาตในพื้นหลังในแต่ละexpires_inมิลลิวินาที

  • พิจารณาว่าเมื่อรอผลลัพธ์ของการโทร api (ทั้งการเข้าสู่ระบบครั้งแรกหรือการรีเฟรช) ผู้ใช้อาจออกจากระบบในระหว่าง

คุณจะนำสิ่งนั้นไปใช้อย่างไรกับชุด; ในขณะที่ยังให้ความคุ้มครองการทดสอบเต็มรูปแบบสำหรับการไหลทั้งหมด? นี่คือลักษณะของ Sagas:

function* authorize(credentials) {
  const token = yield call(api.authorize, credentials)
  yield put( login.success(token) )
  return token
}

function* authAndRefreshTokenOnExpiry(name, password) {
  let token = yield call(authorize, {name, password})
  while(true) {
    yield call(delay, token.expires_in)
    token = yield call(authorize, {token})
  }
}

function* watchAuth() {
  while(true) {
    try {
      const {name, password} = yield take(LOGIN_REQUEST)

      yield race([
        take(LOGOUT),
        call(authAndRefreshTokenOnExpiry, name, password)
      ])

      // user logged out, next while iteration will wait for the
      // next LOGIN_REQUEST action

    } catch(error) {
      yield put( login.error(error) )
    }
  }
}

raceในตัวอย่างข้างต้นเราจะแสดงความเห็นพ้องกับความต้องการของเราโดยใช้ หากtake(LOGOUT)ชนะการแข่งขัน (เช่นผู้ใช้คลิกที่ปุ่มออกจากระบบ) การแข่งขันจะยกเลิกauthAndRefreshTokenOnExpiryงานพื้นหลังโดยอัตโนมัติ และหากauthAndRefreshTokenOnExpiryถูกบล็อกในระหว่างการcall(authorize, {token})โทรมันจะถูกยกเลิกด้วย การยกเลิกจะแพร่กระจายโดยอัตโนมัติ

คุณสามารถค้นหาตัวอย่างที่ทำงานได้ของโฟลว์ด้านบน


@yassine delayฟังก์ชันมาจากไหน อ่าพบแล้ว: github.com/yelouafi/redux-saga/blob/
......

122
redux-thunkรหัสค่อนข้างอ่านและอธิบายด้วยตนเอง แต่redux-sagasหนึ่งไม่สามารถอ่านได้จริงๆส่วนใหญ่เป็นเพราะผู้กริยาเช่นฟังก์ชั่น: call, fork, take, put...
SYG

11
@ syg ฉันเห็นด้วยว่าการโทรการแยกการรับและการวางอาจเป็นมิตรกับความหมายมากกว่า อย่างไรก็ตามมันเป็นฟังก์ชั่นที่เหมือนคำกริยาที่ทำให้ผลข้างเคียงทั้งหมดสามารถทดสอบได้
Downhillski

3
@syg ยังคงฟังก์ชั่นที่มีคำกริยาแปลกเหล่านั้นฟังก์ชั่นสามารถอ่านได้มากกว่าฟังก์ชั่นที่มีห่วงโซ่สัญญาลึก
ยัสเซอร์ Sinjab

3
คำกริยา "แปลก" เหล่านั้นยังช่วยให้คุณสามารถกำหนดความสัมพันธ์ของเทพนิยายกับข้อความที่ออกมาจากการเปลี่ยนรูป คุณสามารถนำประเภทข้อความออกจาก redux - บ่อยครั้งที่จะกระตุ้นการทำซ้ำครั้งถัดไปและคุณสามารถนำข้อความใหม่กลับมาเพื่อถ่ายทอดผลข้างเคียงของคุณ
worc

104

ฉันจะเพิ่มประสบการณ์การใช้ saga ในระบบการผลิตเพิ่มเติมจากคำตอบที่ค่อนข้างสมบูรณ์ของผู้เขียนห้องสมุด

Pro (ใช้เทพนิยาย):

  • การตรวจสอบได้ มันง่ายมากที่จะทดสอบ sagas เมื่อ call () คืนค่าวัตถุบริสุทธิ์ โดยปกติแล้วการทดสอบชุดคุณจะต้องมี mockStore ในการทดสอบของคุณ

  • redux-saga มาพร้อมกับฟังก์ชันผู้ช่วยที่มีประโยชน์มากมายเกี่ยวกับงาน สำหรับฉันแล้วแนวคิดของ saga คือการสร้างพื้นหลัง / เธรดบางส่วนสำหรับแอปของคุณซึ่งทำหน้าที่เป็นชิ้นส่วนที่ขาดหายไปในสถาปัตยกรรมตอบสนองของ redux (actionCreators และ reducers ต้องเป็นฟังก์ชันที่บริสุทธิ์) ซึ่งนำไปสู่ประเด็นต่อไป

  • Sagas เสนอสถานที่อิสระในการจัดการกับผลข้างเคียงทั้งหมด ปกติแล้วการแก้ไขและจัดการจะง่ายกว่าการกระทำอันโหดร้ายในประสบการณ์ของฉัน

Con:

  • ไวยากรณ์ของตัวสร้าง

  • แนวคิดมากมายในการเรียนรู้

  • ความเสถียรของ API ดูเหมือนว่า redux-saga ยังคงเพิ่มคุณสมบัติ (เช่นช่อง?) และชุมชนไม่ใหญ่ มีข้อกังวลหากห้องสมุดทำให้การอัปเดตที่เข้ากันได้แบบไม่ย้อนหลังบางวัน


9
เพียงแค่ต้องการแสดงความคิดเห็นผู้สร้างแอคชั่นไม่จำเป็นต้องมีฟังก์ชั่นที่บริสุทธิ์ซึ่ง Dan เองอ้างสิทธิ์อยู่หลายครั้ง
Marson Mao

14
ณ ตอนนี้แนะนำให้ใช้ redux-sagas เป็นอย่างมากเนื่องจากการใช้งานและชุมชนได้ขยายออกไป ยิ่งไปกว่านั้น API ได้กลายเป็นผู้ใหญ่มากขึ้น พิจารณาลบ Con เพื่อAPI stabilityเป็นการอัพเดทเพื่อสะท้อนถึงสถานการณ์ปัจจุบัน
Denialos

1
เทพนิยายมีจุดเริ่มต้นมากกว่าอันธพาลและความมุ่งมั่นครั้งสุดท้ายของมันก็เกิดขึ้นหลังจากนั้นเช่นกัน
amorenew

2
ใช่ FWIW redux-saga ตอนนี้มีดาว 12k แล้ว redux-thunk มี 8k
Brian Burns

3
ฉันจะเพิ่มความท้าทายอีกอย่างของ sagas คือ sagas นั้นแยกออกจากการกระทำและผู้สร้างแอ็คชั่นโดยสิ้นเชิง ในขณะที่ Thunks เชื่อมโยงผู้สร้างแอ็คชั่นโดยตรงกับผลข้างเคียงของพวกเขาแต่ทว่าออกจากผู้สร้างแอ็คชั่นโดยสิ้นเชิงแยกจากความเป็นจริงที่ฟังพวกเขา สิ่งนี้มีข้อได้เปรียบทางเทคนิค แต่สามารถสร้างโค้ดได้ยากกว่ามากและสามารถทำให้แนวความคิดบางทิศทางไม่ชัดเจน
theaceofthespade

33

ฉันต้องการเพิ่มความคิดเห็นจากประสบการณ์ส่วนตัวของฉัน (ใช้ทั้ง sagas และ thunk):

Sagas นั้นยอดเยี่ยมในการทดสอบ:

  • คุณไม่จำเป็นต้องจำลองฟังก์ชั่นที่ห่อด้วยเอฟเฟกต์
  • ดังนั้นการทดสอบจึงสะอาดอ่านง่ายและเขียนได้ง่าย
  • เมื่อใช้ sagas ผู้สร้างแอ็คชั่นส่วนใหญ่จะส่งคืนตัวอักษรวัตถุธรรมดา นอกจากนี้ยังง่ายต่อการทดสอบและยืนยันซึ่งแตกต่างจากคำมั่นสัญญาของ thunk

Sagas มีพลังมากขึ้น ทุกสิ่งที่คุณสามารถทำได้ในผู้สร้างแอ็คชั่นอันธพาลตัวหนึ่งที่คุณสามารถทำได้ในหนึ่งเทพนิยาย แต่ไม่ใช่ในทางกลับกัน (หรืออย่างน้อยก็ไม่ง่าย) ตัวอย่างเช่น:

  • รอการดำเนินการ / การกระทำที่จะส่ง ( take)
  • ยกเลิกประจำที่มีอยู่ ( cancel, takeLatest, race)
  • กิจวัตรหลายสามารถฟังการกระทำเดียวกัน ( take, takeEvery, ... )

Sagas ยังมีฟังก์ชั่นที่มีประโยชน์อื่น ๆ ซึ่งทำให้รูปแบบของแอปพลิเคชันทั่วไปบางรายการมีลักษณะทั่วไป:

  • channels ฟังแหล่งข้อมูลภายนอก (เช่น websockets)
  • รุ่นส้อม ( fork, spawn)
  • เค้น
  • ...

Sagas เป็นเครื่องมือที่ยอดเยี่ยมและทรงพลัง อย่างไรก็ตามด้วยพลังที่มาพร้อมความรับผิดชอบ เมื่อใบสมัครของคุณเติบโตขึ้นคุณสามารถหลงทางได้ง่ายโดยการหาว่าใครกำลังรอการดำเนินการที่จะส่งมอบหรือสิ่งที่เกิดขึ้นเมื่อมีการส่งการกระทำบางอย่าง ในทางกลับกันความเจริญนั้นง่ายและง่ายกว่าที่จะให้เหตุผล การเลือกอย่างใดอย่างหนึ่งขึ้นอยู่กับหลายแง่มุมเช่นประเภทและขนาดของโครงการผลข้างเคียงของโครงการที่คุณต้องจัดการหรือการตั้งค่าทีม dev ไม่ว่าในกรณีใดเพียงแค่ทำให้แอปพลิเคชันของคุณง่ายและคาดการณ์ได้


8

เพียงประสบการณ์ส่วนตัว:

  1. สำหรับรูปแบบการเข้ารหัสและการอ่านหนึ่งในข้อดีที่สำคัญที่สุดของการใช้ redux-saga ในอดีตคือการหลีกเลี่ยงการเรียกกลับนรกใน redux-thunk - หนึ่งไม่จำเป็นต้องใช้รังจำนวนมากแล้ว / จับอีกต่อไป แต่ตอนนี้ด้วยความนิยมของ async / กำลังรอ thunk อยู่ใครก็สามารถเขียนโค้ด async ในแบบซิงค์ได้เมื่อใช้ redux-thunk ซึ่งอาจถือได้ว่าเป็นการปรับปรุงใน redux-think

  2. หนึ่งอาจต้องเขียนโค้ดสำเร็จรูปมากขึ้นเมื่อใช้ redux-saga โดยเฉพาะใน typescript ตัวอย่างเช่นหากต้องการใช้ฟังก์ชั่นการดึงข้อมูล async การจัดการข้อมูลและข้อผิดพลาดสามารถทำได้โดยตรงในหนึ่งหน่วย thunk ใน action.js ด้วยการกระทำ FETCH เดียว แต่ใน redux-saga เราอาจจำเป็นต้องกำหนดการกระทำของ FETCH_START, FETCH_SUCCESS และ FETCH_FAILURE และการตรวจสอบประเภทที่เกี่ยวข้องทั้งหมดเพราะหนึ่งในคุณสมบัติใน redux-saga คือการใช้กลไกโทเค็นมากมายเพื่อสร้างเอฟเฟกต์และสั่งการ เก็บ redux สำหรับการทดสอบง่าย แน่นอนว่าใคร ๆ ก็สามารถเขียนเทพนิยายได้โดยไม่ต้องใช้การกระทำเหล่านี้ แต่มันจะทำให้มันคล้ายกับความรู้สึก

  3. ในแง่ของโครงสร้างไฟล์ redux-saga ดูเหมือนจะชัดเจนกว่าในหลายกรณี หนึ่งสามารถค้นหารหัสที่เกี่ยวข้อง async ในทุก sagas.ts แต่ใน redux-thunk หนึ่งจะต้องเห็นมันในการกระทำ

  4. การทดสอบอย่างง่ายอาจเป็นอีกคุณสมบัติที่ถ่วงน้ำหนักใน redux-saga นี่คือความสะดวกสบายอย่างแท้จริง แต่สิ่งหนึ่งที่ต้องชี้แจงก็คือการทดสอบ“ call” ของ redux-saga จะไม่ทำการเรียก API จริงในการทดสอบดังนั้นเราจะต้องระบุผลลัพธ์ตัวอย่างสำหรับขั้นตอนที่อาจใช้หลังจากการเรียก API ดังนั้นก่อนที่จะเขียนใน redux-saga จะเป็นการดีกว่าที่จะวางแผน saga และ sagas.spec.ts ให้ละเอียด

  5. Redux-saga ยังมีคุณสมบัติขั้นสูงมากมายเช่นการทำงานในแบบคู่ขนานผู้ช่วยทำงานพร้อมกันเช่น takeLatest / takeEvery, fork / spawn ซึ่งมีประสิทธิภาพมากกว่าก้อน

โดยสรุปส่วนตัวผมอยากจะบอกว่า: ในกรณีปกติจำนวนมากและแอพขนาดเล็กถึงขนาดกลางให้ไปกับ async / สไตล์รอคอย redux-thunk มันจะช่วยให้คุณประหยัดรหัสรหัสการกระทำ / typedefs จำนวนมากและคุณไม่จำเป็นต้องสลับรอบ sagas.ts ที่แตกต่างกันจำนวนมากและรักษาต้นไม้ sagas ที่เฉพาะเจาะจง แต่ถ้าคุณกำลังพัฒนาแอพขนาดใหญ่ที่มีตรรกะแบบอะซิงก์ที่ซับซ้อนมากและความต้องการคุณสมบัติเช่นรูปแบบการทำงานพร้อมกัน / แบบขนานหรือมีความต้องการสูงสำหรับการทดสอบและการบำรุงรักษา .

อย่างไรก็ตาม redux-saga นั้นไม่ยากและซับซ้อนกว่า redux เองและมันไม่มีเส้นโค้งการเรียนรู้ที่สูงชันเพราะมันมีแนวคิดหลักและ API ที่ จำกัด การใช้เวลาเล็กน้อยในการเรียนรู้ redux-saga อาจเป็นประโยชน์ต่อตัวคุณในอนาคต


5

จากการตรวจสอบโครงการ React / Redux ขนาดใหญ่ที่แตกต่างกันในประสบการณ์ของฉัน Sagas ช่วยให้นักพัฒนามีวิธีการเขียนโค้ดที่มีโครงสร้างมากขึ้นซึ่งง่ายต่อการทดสอบและยากที่จะผิด

ใช่มันเป็นเรื่องแปลกที่จะเริ่มต้นด้วย แต่ devs ส่วนใหญ่ได้รับความเข้าใจเพียงพอในหนึ่งวัน ฉันมักจะบอกคนอื่นว่าไม่ต้องกังวลว่าyieldจะเริ่มต้นอย่างไรและเมื่อคุณเขียนการทดสอบสองสามฉบับมันจะมาหาคุณ

ฉันได้เห็นสองโครงการที่ thunks ได้รับการปฏิบัติราวกับว่าพวกเขาเป็นตัวควบคุมจากเสื้อ MVC และสิ่งนี้กลายเป็นความยุ่งเหยิงอย่างรวดเร็ว

คำแนะนำของฉันคือการใช้ Sagas ที่คุณต้องการสิ่งที่เรียกประเภท B ทริกเกอร์ที่เกี่ยวข้องกับเหตุการณ์เดียว สำหรับสิ่งใดก็ตามที่สามารถลดจำนวนการกระทำได้ฉันพบว่าการเขียนมิดเดิลแวร์ของลูกค้าทำได้ง่ายกว่าและใช้คุณสมบัติเมตาของการกระทำ FSA เพื่อเรียกใช้งาน


2

Thunks กับ Sagas

Redux-ThunkและRedux-Sagaแตกต่างกันไปในวิธีการที่สำคัญสองอย่างคือ middleware library สำหรับ Redux (Redux middleware เป็นรหัสที่ขัดขวางการกระทำที่เข้ามาในร้านโดยใช้วิธีการแจกจ่าย ()

การกระทำสามารถเป็นอะไรก็ได้ตามตัวอักษร แต่หากคุณปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดการกระทำนั้นเป็นวัตถุจาวาสคริปต์ธรรมดาที่มีฟิลด์ประเภทและฟิลด์ที่ต้องกรอกข้อมูลเมตาและฟิลด์ข้อผิดพลาด เช่น

const loginRequest = {
    type: 'LOGIN_REQUEST',
    payload: {
        name: 'admin',
        password: '123',
    }, };

Redux-Thunk

นอกเหนือจากการจัดส่งการกระทำมาตรฐานRedux-Thunkมิดเดิลแวร์ช่วยให้คุณสามารถจัดส่งฟังก์ชั่นพิเศษที่เรียกว่าthunksตัวกลางช่วยให้คุณสามารถส่งฟังก์ชั่นพิเศษที่เรียกว่า

Thunks (in Redux) โดยทั่วไปมีโครงสร้างดังต่อไปนี้:

export const thunkName =
   parameters =>
        (dispatch, getState) => {
            // Your application logic goes here
        };

นั่นคือ a thunkคือฟังก์ชั่นที่ (เป็นทางเลือก) รับพารามิเตอร์บางอย่างและส่งกลับฟังก์ชันอื่น ฟังก์ชั่นด้านในใช้ a dispatch functionและgetStateฟังก์ชั่น - ซึ่งทั้งสองอย่างนี้จะได้รับจากRedux-Thunkมิดเดิลแวร์

Redux-Saga

Redux-Sagaมิดเดิลแวร์ช่วยให้คุณสามารถแสดงตรรกะของแอปพลิเคชันที่ซับซ้อนเป็นหน้าที่บริสุทธิ์ที่เรียกว่า sagas ฟังก์ชั่นที่บริสุทธิ์เป็นที่ต้องการจากมุมมองการทดสอบเพราะสามารถคาดเดาและทำซ้ำได้ซึ่งทำให้ง่ายต่อการทดสอบ

Sagas มีการใช้งานผ่านฟังก์ชั่นพิเศษที่เรียกว่าฟังก์ชั่นเครื่องกำเนิดไฟฟ้า ES6 JavaScriptเหล่านี้เป็นคุณลักษณะใหม่ของ โดยทั่วไปการประมวลผลจะกระโดดเข้าและออกจากเครื่องกำเนิดไฟฟ้าทุกที่ที่คุณเห็นข้อความประกาศผลตอบแทน ลองนึกถึงyieldข้อความที่ทำให้เครื่องกำเนิดหยุดและส่งคืนค่าที่ได้ หลังจากนั้นผู้เรียกสามารถเรียกตัวสร้างขึ้นมาใหม่ได้ที่คำสั่งต่อไปนี้yieldต่อมาเมื่อโทรสามารถดำเนินการต่อเครื่องกำเนิดไฟฟ้าที่คำสั่งดังต่อไปนี้

ฟังก์ชั่นเครื่องกำเนิดไฟฟ้าเป็นหนึ่งที่กำหนดไว้เช่นนี้ สังเกตุเครื่องหมายดอกจันหลังคีย์เวิร์ดของฟังก์ชัน

function* mySaga() {
    // ...
}

Redux-Sagaเมื่อวีรชนเข้าสู่ระบบลงทะเบียนกับ แต่จากนั้นyieldใช้ในบรรทัดแรกจะหยุดการผจญภัยจนกว่าการกระทำกับประเภท'LOGIN_REQUEST'ถูกส่งไปยังร้านค้า เมื่อเกิดเหตุการณ์ขึ้นการดำเนินการจะดำเนินต่อไป

สำหรับรายละเอียดเพิ่มเติมดูบทความนี้


1

โน้ตย่อหนึ่งอัน เครื่องกำเนิดไฟฟ้าสามารถยกเลิกได้ async / คอย - ไม่ใช่ ดังนั้นสำหรับตัวอย่างจากคำถามมันไม่สมเหตุสมผลเลยที่จะเลือก แต่สำหรับการไหลที่ซับซ้อนมากขึ้นบางครั้งก็ไม่มีทางออกที่ดีไปกว่าการใช้เครื่องกำเนิดไฟฟ้า

ดังนั้นความคิดอีกอย่างหนึ่งก็คือการใช้เครื่องกำเนิดไฟฟ้าที่มี Redux-thunk แต่สำหรับฉันดูเหมือนว่าจะพยายามประดิษฐ์จักรยานด้วยล้อสี่เหลี่ยม

และแน่นอนเครื่องกำเนิดไฟฟ้าทดสอบได้ง่ายกว่า


0

นี่เป็นโครงการที่รวมส่วนที่ดีที่สุด (ข้อดี) ของทั้งสองredux-sagaและredux-thunkคุณสามารถจัดการกับผลข้างเคียงทั้งหมดเกี่ยวกับโศกนาฏกรรมในขณะที่รับสัญญาโดยdispatchingการดำเนินการที่สอดคล้องกัน: https://github.com/diegohaz/redux-saga-thunk

class MyComponent extends React.Component {
  componentWillMount() {
    // `doSomething` dispatches an action which is handled by some saga
    this.props.doSomething().then((detail) => {
      console.log('Yaay!', detail)
    }).catch((error) => {
      console.log('Oops!', error)
    })
  }
}

1
การใช้then()ภายในองค์ประกอบของ React ขัดต่อกระบวนทัศน์ คุณควรจัดการกับสถานะที่เปลี่ยนแปลงcomponentDidUpdateแทนที่จะรอให้สัญญาได้รับการแก้ไข

3
@ Maxincredible52 ไม่เป็นความจริงสำหรับการแสดงผลฝั่งเซิร์ฟเวอร์
Diego Haz

จากประสบการณ์ของผมประเด็นของ Max ยังคงเป็นจริงสำหรับการเรนเดอร์ฝั่งเซิร์ฟเวอร์ เรื่องนี้ควรได้รับการจัดการที่ไหนสักแห่งในเส้นทางเลเยอร์
ThinkingInBits

3
@ Maxincredible52 ทำไมมันขัดกับกระบวนทัศน์ที่คุณเคยอ่านที่? ฉันมักจะทำคล้ายกับ @Diego Haz แต่ทำใน componentDidMount (ตามเอกสาร React การโทรผ่านเครือข่ายควรจะดีกว่าที่นั่น) ดังนั้นเราจึงมีcomponentDidlMount() { this.props.doSomething().then((detail) => { this.setState({isReady: true})} }
user3711421

0

วิธีที่ง่ายกว่าคือใช้redux-auto Redux

จาก documantasion

redux-auto แก้ไขปัญหาแบบอะซิงโครนัสเพียงแค่ให้คุณสร้างฟังก์ชั่น "การกระทำ" ที่ส่งคืนสัญญา เพื่อติดตามตรรกะการทำงานของฟังก์ชัน "ค่าเริ่มต้น"

  1. ไม่จำเป็นสำหรับมิดเดิลแวร์ Redux async อื่น ๆ เช่น thunk, middleware ที่สัญญา, saga
  2. อนุญาตให้คุณส่งสัญญาไปยัง redux และจัดการให้คุณได้อย่างง่ายดาย
  3. ช่วยให้คุณสามารถค้นหาบริการภายนอกด้วยการที่พวกเขาจะถูกเปลี่ยน
  4. การตั้งชื่อไฟล์ "init.js" จะเรียกมันครั้งเดียวเมื่อเริ่มต้นแอพ เป็นการดีสำหรับการโหลดข้อมูลจากเซิร์ฟเวอร์เมื่อเริ่มต้น

แนวคิดคือการมีแต่ละการกระทำในไฟล์เฉพาะการกระทำในแฟ้มที่เฉพาะเจาะจงค้นหาการเรียกเซิร์ฟเวอร์ในไฟล์ที่มีฟังก์ชั่นลดสำหรับ "รอดำเนินการ", "เติมเต็ม" และ "ปฏิเสธ" ทำให้การจัดการเป็นเรื่องง่ายมาก

นอกจากนี้ยังแนบวัตถุตัวช่วยโดยอัตโนมัติ(เรียกว่า "async")เข้ากับต้นแบบของรัฐของคุณทำให้คุณสามารถติดตามใน UI ของคุณการเปลี่ยนที่ร้องขอ


2
ฉันทำ +1 แม้ว่าจะเป็นคำตอบที่ไม่เกี่ยวข้องเพราะควรพิจารณาวิธีการแก้ปัญหาที่แตกต่างกันด้วย
amorenew

12
ฉันคิดว่าของที่นั่นเพราะเขาไม่ได้เปิดเผยว่าเขาเป็นผู้เขียนของโครงการ
jreptak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.