วิธีที่ดีที่สุดในการจัดการกับข้อผิดพลาดในการดึงข้อมูลใน react redux คืออะไร?


105

ฉันมีตัวลดหนึ่งตัวสำหรับไคลเอนต์อีกตัวหนึ่งสำหรับ AppToolbar และอื่น ๆ ...

ตอนนี้สมมติว่าฉันสร้างการดำเนินการดึงข้อมูลเพื่อลบไคลเอนต์และหากล้มเหลวฉันมีโค้ดในตัวลดไคลเอ็นต์ซึ่งควรทำบางอย่าง แต่ฉันต้องการแสดงข้อผิดพลาดส่วนกลางใน AppToolbar ด้วย

แต่ไคลเอนต์และตัวลด AppToolbar ไม่แชร์ส่วนเดียวกันของสถานะและฉันไม่สามารถสร้างการดำเนินการใหม่ในตัวลด

แล้วฉันจะแสดงข้อผิดพลาดทั่วโลกได้อย่างไร? ขอบคุณ

อัปเดต 1:

ลืมบอกไปว่าใช้este devstack

อัปเดต 2: ฉันทำเครื่องหมายคำตอบของ Eric ว่าถูกต้อง แต่ฉันต้องบอกว่าวิธีแก้ปัญหาที่ฉันใช้ใน este นั้นเหมือนกับการรวมกันของคำตอบของ Eric และ Dan ... คุณต้องหาสิ่งที่เหมาะกับคุณที่สุดในรหัสของคุณ .. .


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

ฉันต้องยอมรับ w / @acjay ว่าคำถามนี้ขาดบริบท ฉันได้ตอบไว้ด้านล่าง (พร้อมตัวอย่างโค้ด) พร้อมวิธีแก้ปัญหาทั่วไป แต่คำถามของคุณอาจใช้การปรับแต่งบางอย่าง ดูเหมือนว่าคุณอาจมีปัญหาแยกกันเล็กน้อย 1) การจัดการการกระทำ / ข้อผิดพลาด async 2) การแยกสถานะอย่างเหมาะสมในโครงสร้างสถานะ redux ของคุณ 3) รับส่วนประกอบของคุณตามข้อมูลที่ต้องการ
Erik Aybar

@ErikTheDeveloper ขอบคุณคำตอบของคุณดูดีมาก แต่คุณพูดถูกฉันลืมพูดถึงบริบท ฉันแก้ไขคำถามของฉันฉันใช้ este devstack และดูเหมือนว่าคำตอบของคุณจะใช้ไม่ได้เหมือนที่เป็นอยู่ ...
Dusan Plavak

คำตอบ:


117

หากคุณต้องการมีแนวคิดของ "global error" คุณสามารถสร้างตัวerrorsลดซึ่งสามารถรับฟังการดำเนินการ addError, removeError ฯลฯ ... จากนั้นคุณสามารถเชื่อมต่อกับต้นไม้สถานะ Redux ของคุณที่state.errorsและแสดงได้ทุกที่ที่เหมาะสม

มีหลายวิธีที่คุณสามารถใช้วิธีนี้มี แต่ความคิดทั่วไปคือว่าข้อผิดพลาดระดับโลก / ข้อความจะทำบุญลดของตัวเองจะมีชีวิตอยู่อย่างสมบูรณ์แยกออกจาก/<Clients /> <AppToolbar />แน่นอนว่าหากส่วนประกอบเหล่านี้ต้องการการเข้าถึงerrorsคุณสามารถส่งerrorsต่อไปยังส่วนประกอบเหล่านี้ได้ทุกที่ที่ต้องการ

อัปเดต: ตัวอย่างโค้ด

นี่คือตัวอย่างหนึ่งของลักษณะที่อาจเป็นไปได้หากคุณส่ง "ข้อผิดพลาดร่วม" errorsไปยังระดับบนสุดของคุณ<App />และแสดงผลตามเงื่อนไข (หากมีข้อผิดพลาดอยู่) ใช้react-reduxconnectเพื่อเชื่อมต่อ<App />ส่วนประกอบของคุณกับข้อมูลบางอย่าง

// App.js
// Display "global errors" when they are present
function App({errors}) {
  return (
    <div>
      {errors && 
        <UserErrors errors={errors} />
      }
      <AppToolbar />
      <Clients />
    </div>
  )
}

// Hook up App to be a container (react-redux)
export default connect(
  state => ({
    errors: state.errors,
  })
)(App);

และเท่าที่ผู้สร้างการกระทำเกี่ยวข้องมันจะส่งความล้มเหลวของความสำเร็จ( ซ้ำซ้อน ) ไปตามการตอบสนอง

export function fetchSomeResources() {
  return dispatch => {
    // Async action is starting...
    dispatch({type: FETCH_RESOURCES});

    someHttpClient.get('/resources')

      // Async action succeeded...
      .then(res => {
        dispatch({type: FETCH_RESOURCES_SUCCESS, data: res.body});
      })

      // Async action failed...
      .catch(err => {
        // Dispatch specific "some resources failed" if needed...
        dispatch({type: FETCH_RESOURCES_FAIL});

        // Dispatch the generic "global errors" action
        // This is what makes its way into state.errors
        dispatch({type: ADD_ERROR, error: err});
      });
  };
}

แม้ว่าตัวลดของคุณสามารถจัดการอาร์เรย์ของข้อผิดพลาดได้ แต่การเพิ่ม / ลบรายการอย่างเหมาะสม

function errors(state = [], action) {
  switch (action.type) {

    case ADD_ERROR:
      return state.concat([action.error]);

    case REMOVE_ERROR:
      return state.filter((error, i) => i !== action.index);

    default:
      return state;
  }
}

1
เอริคผมมีสิ่งที่คล้ายกับสิ่งที่คุณได้แนะนำที่นี่ แต่ที่น่าแปลกใจฉันไม่เคยจัดการเพื่อให้ได้catchฟังก์ชั่นที่เรียกว่าถ้าsomeHttpClient.get('/resources')หรือที่ฉันใช้ในการกลับมารหัสของฉันfetch('/resources') 500 Server Errorคุณมีความคิดใด ๆ ในหัวของคุณที่ฉันอาจทำผิดพลาดหรือไม่? โดยพื้นฐานแล้วสิ่งที่ฉันทำคือfetchส่งคำขอซึ่งลงท้ายด้วยroutesสิ่งที่ฉันเรียกว่าวิธีการในmongooseแบบจำลองของฉันสำหรับการทำสิ่งที่ง่ายมากเช่นการเพิ่มข้อความหรือลบข้อความออกจากฐานข้อมูล
Kevin Ghaboosi

3
สวัสดีฉันมาที่นี่จากการค้นหาของ Google - แค่อยากขอบคุณสำหรับตัวอย่างที่ดี .. ฉันประสบปัญหาเดิม ๆ และนี่ก็ยอดเยี่ยมมาก แน่นอนวิธีแก้ไขคือการรวมข้อผิดพลาดลงในร้านค้า ทำไมฉันไม่คิดแบบนั้น ... ไชโย
Spock

2
หนึ่งจะเรียกใช้ฟังก์ชันอย่างไรเมื่อเกิดข้อผิดพลาด? เช่นฉันต้องแสดงขนมปัง / การแจ้งเตือนใน UI ไม่ใช่การแสดงผลองค์ประกอบการแจ้งเตือนโดยการอัปเดตอุปกรณ์ประกอบฉากขององค์ประกอบหลัก
Gianfranco P.

112

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

ตัวอย่างเช่นจากตัวอย่างReduxreal-worldที่มีการจัดการข้อผิดพลาด:

// Updates error message to notify about the failed fetches.
function errorMessage(state = null, action) {
  const { type, error } = action

  if (type === ActionTypes.RESET_ERROR_MESSAGE) {
    return null
  } else if (error) {
    return error
  }

  return state
}

หมายความว่าคำขอความสำเร็จทุกครั้งที่เราควรส่งประเภท RESET_ERROR_MESSAGE ไปยัง errorMessage reducer หรือไม่
Dimi Mikadze

2
@DimitriMikadze ไม่มันไม่ได้ ฟังก์ชันนี้เป็นเพียงตัวลดสถานะข้อผิดพลาด หากคุณผ่าน RESET_ERROR_MESSAGE จะเป็นการล้างข้อความแสดงข้อผิดพลาดทั้งหมด หากคุณไม่ผ่านและไม่มีฟิลด์ข้อผิดพลาดมันก็จะคืนสถานะไม่เปลี่ยนแปลงดังนั้นหากมีข้อผิดพลาดจากการกระทำก่อนหน้านี้ข้อผิดพลาดเหล่านี้จะยังคงอยู่ที่นั่นหลังจากการดำเนินการสำเร็จ ....
Dusan Plavak

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

1
ฉันไม่ค่อยเข้าใจวิธีการทำงานนี้ นอกเหนือจากตัวอย่างในโลกแห่งความเป็นจริงแล้วคุณมีเอกสาร / วิดีโอที่อธิบายเรื่องนี้หรือไม่ เป็นความต้องการหลักของโครงการส่วนใหญ่และฉันพบเอกสารประกอบเกี่ยวกับเรื่องนี้เพียงเล็กน้อย ขอบคุณ.
Matt Saunders

6
@MattSaunders ในขณะที่พยายามทำความเข้าใจฉันได้พบกับหลักสูตร Redux โดย Dan เอง (ผู้ตอบซึ่งเป็นผู้สร้าง Redux) โดยมีส่วนเกี่ยวกับการแสดงข้อความแสดงข้อผิดพลาดซึ่งพร้อมกับคำตอบเหล่านี้และตัวอย่างในโลกแห่งความจริงทำให้มันกลับบ้าน ผม. โชคดี.
Agustín Lado

2

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

ดังนั้น:

function rootReducer(state, action) {
  try {
    // sub-reducer(s)
    state = someOtherReducer(state,action);
  } catch (e) {
    action.error = e;
  }
  return state;
}

// and then in the saga, registered to take every action:
function *errorHandler(action) {
  if (action.error) {
     yield put(errorActionCreator(error));
  }
}

จากนั้นการเพิ่มข้อผิดพลาดให้กับแผนภูมิสถานะเป็นไปตามที่ Erik อธิบาย

ฉันใช้มันค่อนข้าง จำกัด แต่มันทำให้ฉันไม่ต้องทำซ้ำตรรกะที่ถูกต้องตามกฎหมายในตัวลด (เพื่อป้องกันตัวเองจากสถานะที่ไม่ถูกต้อง)


1

เขียนมิดเดิลแวร์แบบกำหนดเองเพื่อจัดการข้อผิดพลาดที่เกี่ยวข้องกับ api ทั้งหมด ในกรณีนี้รหัสของคุณจะสะอาดยิ่งขึ้น

   failure/ error actin type ACTION_ERROR

   export default  (state) => (next) => (action) => {

      if(ACTION_ERROR.contains('_ERROR')){

       // fire error action
        store.dispatch(serviceError());

       }
}

2
ยังทำให้การดีบัก IMHO ยากขึ้นอีกด้วย
chrisjlee

3
คุณไม่จำเป็นต้องใช้มิดเดิลแวร์สำหรับสิ่งนี้คุณสามารถเขียนสิ่งเดียวกันifในตัวลด
Juan Campa

หากมีมากกว่า 50 api คุณต้องเขียนในทุกที่ แต่คุณสามารถเขียนมิดเดิลแวร์ที่กำหนดเองเพื่อตรวจสอบข้อผิดพลาดได้
Shrawan

0

สิ่งที่ฉันทำคือฉันรวมศูนย์การจัดการข้อผิดพลาดทั้งหมดในเอฟเฟกต์บนพื้นฐานผลกระทบ

/**
 * central error handling
 */
@Effect({dispatch: false})
httpErrors$: Observable<any> = this.actions$
    .ofType(
        EHitCountsActions.HitCountsError
    ).map(payload => payload)
    .switchMap(error => {
        return of(confirm(`There was an error accessing the server: ${error}`));
    });

-8

คุณสามารถใช้ไคลเอนต์ axios HTTP ได้ใช้คุณสมบัติ Interceptors แล้ว คุณสามารถสกัดกั้นคำขอหรือการตอบกลับก่อนที่จะจัดการหรือถูกจับได้

https://github.com/mzabriskie/axios#interceptors

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });


ใช่ แต่คุณไม่ได้ส่งอะไรไปยัง redux?
Eino Mäkitalo

แนวทางนี้ไม่เลว โดยปกติการจัดเก็บใน redux คือซิงเกิลตันและคุณสามารถอิมพอร์ตสโตร์ในไฟล์ axios interceptors และใช้ store.dispatch () เพื่อทริกเกอร์การดำเนินการใด ๆ นี่เป็นวิธีการแบบรวมเพื่อจัดการข้อผิดพลาด api ทั้งหมดในระบบในที่
Wedmich
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.