จะแก้ไขคำเตือนพึ่งพาเมื่อใช้ useEffect React Hook ได้อย่างไร


180

ด้วย React 16.8.6 (ดีในเวอร์ชันก่อนหน้า 16.8.3) ฉันได้รับข้อผิดพลาดนี้เมื่อฉันพยายามป้องกันลูปไม่สิ้นสุดในคำขอดึงข้อมูล

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

ฉันไม่พบวิธีแก้ปัญหาที่หยุดลูปไม่สิ้นสุด useReducer()ผมต้องการที่จะอยู่ห่างจากการใช้ ฉันพบการสนทนานี้https://github.com/facebook/react/issues/14920ซึ่งการแก้ปัญหาที่เป็นไปได้คือYou can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.ฉันไม่มั่นใจในสิ่งที่ฉันทำดังนั้นฉันยังไม่ได้ลองใช้งานเลย

ฉันมีการตั้งค่าปัจจุบันนี้React hook useEffect จะทำงานอย่างต่อเนื่องตลอดไป / วนซ้ำไม่สิ้นสุดและความคิดเห็นเพียงอย่างเดียวคือuseCallback()สิ่งที่ฉันไม่คุ้นเคย

วิธีที่ฉันใช้อยู่ในปัจจุบันuseEffect()(ซึ่งฉันต้องการเรียกใช้ครั้งเดียวในจุดเริ่มต้นที่คล้ายกับcomponentDidMount())

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

คำตอบ:


193

หากคุณไม่ได้ใช้วิธี fetchBusinesses นอกเหนือจากเอฟเฟกต์คุณสามารถย้ายมันไปเป็นเอฟเฟกต์และหลีกเลี่ยงการเตือนได้

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

หากคุณใช้ fetchBusinesses นอกการเรนเดอร์คุณต้องจดบันทึกสองสิ่ง

  1. มีปัญหากับคุณที่ไม่ผ่านfetchBusinessesเป็นวิธีการเมื่อใช้ระหว่างการเมานท์ที่มีการปิดล้อมหรือไม่?
  2. วิธีการของคุณขึ้นอยู่กับตัวแปรบางตัวที่ได้รับจากการปิดล้อมหรือไม่? นี่ไม่ใช่กรณีสำหรับคุณ
  3. ในทุกการแสดงผล fetchBusinesses จะถูกสร้างขึ้นใหม่และด้วยเหตุนี้การส่งผ่านไปยัง useEffect จะทำให้เกิดปัญหา ดังนั้นก่อนอื่นคุณต้องจดจำ fetchBusinesses ถ้าคุณต้องส่งมันไปยังอาร์เรย์การพึ่งพา

สรุปแล้วฉันจะบอกว่าถ้าคุณใช้fetchBusinessesนอกuseEffectคุณสามารถปิดการใช้งานกฎ// eslint-disable-next-line react-hooks/exhaustive-depsมิฉะนั้นคุณสามารถย้ายวิธีการภายในการใช้งานผล

ปิดการใช้งานกฎที่คุณจะเขียนมันชอบ

useEffect(() => {
   // other code
   ...

   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 

14
ฉันใช้โซลูชันที่คุณระบุไว้เป็นอย่างดี useCallback()วิธีการแก้ปัญหาอีกประการหนึ่งที่ผมใช้ที่อื่นเนื่องจากมีการตั้งค่าที่แตกต่างกันคือ ดังนั้นสำหรับตัวอย่างเช่น const fetchBusinesses= useCallback(() => { ... }, [...]) และuseEffect()จะมีลักษณะเช่นนี้:useEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);
รัส

1
@ รัสคุณถูกต้องคุณจะต้องจดจำ fetchBusiness โดยใช้ useCallback ถ้าคุณจะส่งผ่านไปยังอาร์เรย์พึ่งพา
Shubham Khatri

มันจะดีถ้าคุณแสดงให้เห็นว่าจะใส่คำสั่ง eslint-disable ฉันคิดว่ามันน่าจะใช้ประโยชน์ได้มากกว่า
210757

1
การใช้// eslint-disable-next-line react-hooks/exhaustive-depsเพื่ออธิบายต่อ linter ว่ารหัสของคุณถูกต้องนั้นเหมือนแฮ็ค ฉันคาดหวังว่าพวกเขาจะหาวิธีแก้ไขปัญหาอื่นเพื่อทำให้การใช้งานอย่างชาญฉลาดมากขึ้นพอที่จะตรวจพบเมื่อข้อโต้แย้งไม่ได้รับคำสั่ง
Olivier Boissé

1
@ TapasAdhikary ใช่คุณสามารถมีฟังก์ชั่น async ใน useEffect คุณเพียงแค่ต้องเขียนมันต่างกัน โปรดตรวจสอบstackoverflow.com/questions/53332321/…
Shubham Khatri

75
./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

ไม่ใช่ข้อผิดพลาด JS / React แต่มีคำเตือน eslint (eslint-plugin-react-hooks)

กำลังบอกคุณว่า hook ขึ้นอยู่กับฟังก์ชันfetchBusinessesดังนั้นคุณควรผ่านมันไปเป็นการพึ่งพา

useEffect(() => {
  fetchBusinesses();
}, [fetchBusinesses]);

มันอาจส่งผลให้ฟังก์ชั่นการเรียกใช้ทุกการแสดงผลถ้าฟังก์ชั่นมีการประกาศในองค์ประกอบเช่น:

const Component = () => {
  /*...*/

  //new function declaration every render
  const fetchBusinesses = () => {
    fetch('/api/businesses/')
      .then(...)
  }

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

เพราะทุกครั้งที่มีการประกาศฟังก์ชันใหม่ด้วยการอ้างอิงใหม่

วิธีที่ถูกต้องในการทำสิ่งนี้คือ:

const Component = () => {
  /*...*/

  // keep function reference
  const fetchBusinesses = useCallback(() => {
    fetch('/api/businesses/')
      .then(...)
  }, [/* additional dependencies */]) 

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

หรือเพียงแค่กำหนดฟังก์ชันใน useEffect

เพิ่มเติมได้ที่: https://github.com/facebook/react/issues/14920


14
ผลนี้ในข้อผิดพลาดใหม่Line 20: The 'fetchBusinesses' function makes the dependencies of useEffect Hook (at line 51) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'fetchBusinesses' definition into its own useCallback() Hook
รัส

1
การแก้ปัญหาคือดีและถ้าฟังก์ชั่นที่คุณแก้ไขรัฐอื่นคุณจะต้องเพิ่ม dependecies เพื่อหลีกเลี่ยงพฤติกรรมที่ไม่คาดคิดอีก
cesarlarsson

58

คุณสามารถตั้งค่าโดยตรงเป็นการuseEffectโทรกลับ:

useEffect(fetchBusinesses, [])

มันจะทริกเกอร์เพียงครั้งเดียวดังนั้นตรวจสอบให้แน่ใจว่าการตั้งค่าการอ้างอิงของฟังก์ชันทั้งหมดถูกต้อง (เหมือนกับการใช้componentDidMount/componentWillMount...)


แก้ไข 02/21/2020

เพียงเพื่อความสมบูรณ์:

1. ใช้ฟังก์ชั่นเป็นการuseEffectโทรกลับ (ตามด้านบน)

useEffect(fetchBusinesses, [])

2. ประกาศฟังก์ชั่นภายใน useEffect()

useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3. จดจำด้วย useCallback()

ในกรณีนี้ถ้าคุณมีการพึ่งพาในการทำงานของคุณคุณจะต้องรวมไว้ในuseCallbackอาร์เรย์การพึ่งพาและสิ่งนี้จะทริกเกอร์useEffectอีกครั้งหากการเปลี่ยนแปลงพารามิเตอร์ของฟังก์ชั่น นอกจากนี้มันยังมีหม้อไอน้ำจำนวนมาก ... ดังนั้นเพียงแค่ส่งฟังก์ชั่นไปยังuseEffectเช่นเดียวกับใน1. useEffect(fetchBusinesses, [])โดยตรง

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4. ปิดการเตือนของ eslint

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps

2
ฉันรักคุณ ... คำตอบนี้เสร็จสมบูรณ์แล้ว!
Nick09

8

วิธีการแก้ปัญหายังได้รับจากปฏิกิริยาพวกเขาแนะนำให้คุณใช้useCallbackซึ่งจะส่งกลับรุ่นบันทึกการทำงานของคุณ:

ฟังก์ชัน 'fetchBusinesses' ทำให้การขึ้นต่อกันของ useEffect Hook (ที่บรรทัด NN) เปลี่ยนแปลงทุกการแสดงผล ในการแก้ไขปัญหานี้ให้ตัดคำนิยาม 'fetchBusinesses' ลงใน useCallback () Hook hooks-hooks / exhaustive-dep

useCallbackใช้งานง่ายเพราะมีลายเซ็นเหมือนuseEffectกันกับความแตกต่างคือ useCallback ส่งคืนฟังก์ชัน มันจะมีลักษณะเช่นนี้:

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(() => { /* some stuff */ })
    .catch(() => { /* some error handling */ })
  }, [/* deps */])
  // We have a first effect thant uses fetchBusinesses
  useEffect(() => {
    // do things and then fetchBusinesses
    fetchBusinesses(); 
  }, [fetchBusinesses]);
   // We can have many effect thant uses fetchBusinesses
  useEffect(() => {
    // do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);

2

คำเตือนเหล่านี้มีประโยชน์มากสำหรับการค้นหาส่วนประกอบที่ไม่อัปเดตอย่างสม่ำเสมอ: https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of- การอ้างอิง

อย่างไรก็ตามหากคุณต้องการลบคำเตือนทั่วทั้งโครงการของคุณคุณสามารถเพิ่มสิ่งนี้ในการกำหนดค่า eslint ของคุณ:

  {
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/exhaustive-deps": 0
    }
  }

1

บทความนี้เป็นไพรเมอร์ที่ดีในการดึงข้อมูลด้วย hooks: https://www.robinwieruch.de/react-hooks-fetch-data/

เป็นหลักรวมถึงการกำหนดฟังก์ชั่นการดึงข้อมูลภายในuseEffect:

useEffect(() => {
  const fetchBusinesses = () => {
    return fetch("theUrl"...
      // ...your fetch implementation
    );
  }

  fetchBusinesses();
}, []);

1

คุณสามารถลบอาเรย์ประเภทที่ 2 ได้[]แต่fetchBusinesses()จะเรียกว่าการอัพเดททุกครั้ง คุณสามารถเพิ่มIFคำสั่งในfetchBusinesses()การนำไปใช้หากคุณต้องการ

React.useEffect(() => {
  fetchBusinesses();
});

อีกอันหนึ่งคือการใช้fetchBusinesses()ฟังก์ชั่นนอกองค์ประกอบของคุณ อย่าลืมส่งอาร์กิวเมนต์ที่ต้องพึ่งพาไปยังการfetchBusinesses(dependency)โทรของคุณหากมี

function fetchBusinesses (fetch) {
  return fetch("theURL", { method: "GET" })
    .then(res => normalizeResponseErrors(res))
    .then(res => res.json())
    .then(rcvdBusinesses => {
      // some stuff
    })
    .catch(err => {
      // some error handling
    });
}

function YourComponent (props) {
  const { fetch } = props;

  React.useEffect(() => {
    fetchBusinesses(fetch);
  }, [fetch]);

  // ...
}

0

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

หากคุณไม่ต้องการใส่fetchBusinessesในการขึ้นต่อกันของ hook คุณสามารถส่งมันเป็นอาร์กิวเมนต์ไปที่ callback ของ hook และตั้งค่า main fetchBusinessesเป็นค่าเริ่มต้นสำหรับมัน

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

ไม่ใช่วิธีปฏิบัติที่ดีที่สุดแต่อาจมีประโยชน์ในบางกรณี

เช่นเดียวกับที่ Shubnam เขียนคุณสามารถเพิ่มโค้ดด้านล่างเพื่อบอก ESLint ให้ละเว้นการตรวจสอบสำหรับ hook ของคุณ

// eslint-disable-next-line react-hooks/exhaustive-deps

0

ฉันต้องการเรียกใช้ [ fetchBusinesses] เพียงครั้งเดียวในการเริ่มต้นที่คล้ายกับcomponentDidMount()

คุณสามารถดึงfetchBusinessesส่วนประกอบของคุณออกได้อย่างสมบูรณ์:

const fetchBusinesses = () => { // or pass some additional input from component as args
  return fetch("theURL", { method: "GET" }).then(n => process(n));
};

const Comp = () => {
  React.useEffect(() => {
    fetchBusinesses().then(someVal => {
      // ... do something with someVal
    });
  }, []); // eslint warning solved!
  return <div>{state}</div>;
};

สิ่งนี้จะไม่เพียง แต่ให้โซลูชันที่ง่ายและแก้ไขคำเตือนแบบละเอียด fetchBusinessตอนนี้สามารถทดสอบได้ดีขึ้นและทำให้ง่ายขึ้นCompเนื่องจากมันอยู่ในขอบเขตของโมดูลนอกทรีทรี

การย้ายที่fetchBusinessesอยู่ภายนอกทำงานได้ดีที่นี่เนื่องจากเราจะสามารถอ่านอุปกรณ์ประกอบฉากเริ่มต้นและสถานะจากองค์ประกอบต่อไปได้เนื่องจากขอบเขตการปิดเก่า ( []dep in useEffect)

วิธีละเว้นการขึ้นต่อกันของฟังก์ชัน

  • ย้ายฟังก์ชันภายในเอฟเฟกต์
  • ย้ายฟังก์ชั่นนอกองค์ประกอบ - (เราใช้อันนี้)
  • ฟังก์ชั่นการโทรในระหว่างการเรนเดอร์และปล่อยให้useEffectขึ้นอยู่กับค่านี้ (ฟังก์ชันการคำนวณแบบบริสุทธิ์)
  • เพิ่มฟังก์ชั่นที่จะมีผล deps และห่อด้วยuseCallbackเป็นวิธีสุดท้าย

เกี่ยวกับการแก้ปัญหาอื่น ๆ :

การดึงfetchBusinessesเข้าไปด้านในuseEffect()นั้นไม่ได้ช่วยอะไรเลยจริงๆถ้าคุณเข้าถึงสถานะอื่น eslint ยังคงบ่น: Codesandbox

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


0
const [mount, setMount] = useState(false)
const fetchBusinesses = () => { 
   //function defination
}
useEffect(() => {
   if(!mount) {
      setMount(true);
      fetchBusinesses();
   }
},[fetchBusinesses]);

นี่เป็นวิธีการแก้ปัญหาที่ค่อนข้างง่ายและคุณไม่จำเป็นต้องแทนที่คำเตือน es-lint เพียงแค่รักษาธงเพื่อตรวจสอบว่ามีการติดตั้งส่วนประกอบหรือไม่


0

คุณลองวิธีนี้

const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

และ

useEffect(() => {
    fetchBusinesses();
  });

มันเหมาะกับคุณ แต่ข้อเสนอแนะของฉันคือลองวิธีนี้ได้ผลกับคุณเช่นกัน มันดีกว่าเมื่อก่อน ฉันใช้วิธีนี้:

useEffect(() => {
        const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
        fetchBusinesses();
      }, []);

หากคุณได้รับข้อมูลบนฐานของ id ที่เฉพาะเจาะจงแล้วเพิ่มใน callback useEffect [id]แล้วไม่สามารถแสดงคำเตือน React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array


-4

เพียงปิดการใช้งาน eslint สำหรับบรรทัดถัดไป

useEffect(() => {
   fetchBusinesses();
// eslint-disable-next-line
}, []);

ด้วยวิธีนี้คุณจะใช้งานได้เช่นเดียวกับส่วนประกอบที่เมาท์ (เรียกครั้งเดียว)

อัปเดต

หรือ

const fetchBusinesses = useCallback(() => {
 // your logic in here
 }, [someDeps])

useEffect(() => {
   fetchBusinesses();
// no need to skip eslint warning
}, [fetchBusinesses]); 

fetchBusinesses จะถูกเรียกทุกครั้งที่บางคนเปลี่ยนไป


แทนที่จะปิดใช้งานให้ทำดังนี้: [fetchBusinesses]จะลบคำเตือนโดยอัตโนมัติและจะแก้ไขปัญหาให้ฉัน
rotimi ที่ดีที่สุด

7
@RotimiBest - การทำเช่นนี้จะทำให้การเรนเดอร์ไม่สิ้นสุดดังที่อธิบายไว้ในคำถาม
user210757

จริง ๆ แล้วฉันทำแบบนี้ในโครงการของฉันเมื่อไม่นานมานี้และมันไม่ได้สร้างวงวนไม่สิ้นสุด ฉันจะตรวจสอบอีกครั้งว่า
ดีที่สุดของ

@ user210757 รอสักครู่ แต่ทำไมมันจะทำให้เกิดการวนซ้ำไม่สิ้นสุดมันไม่เหมือนคุณกำลังตั้งค่าสถานะหลังจากดึงข้อมูลจากเซิร์ฟเวอร์ หากคุณกำลังอัพเดตสถานะเพียงแค่เขียนเงื่อนไข if ก่อนเรียกใช้ฟังก์ชันในการuseEffectตรวจสอบว่าสถานะนั้นว่างเปล่าหรือไม่
rotimi ที่ดีที่สุด

@ rotimi ที่ดีที่สุดคือ ahwile ตั้งแต่ฉันแสดงความคิดเห็น แต่ฉันจะบอกว่าฟังก์ชั่นนี้ถูกสร้างขึ้นใหม่ทุกครั้งดังนั้นจึงไม่เหมือนเดิมดังนั้นจะทำการเรนเดอร์ใหม่เสมอเว้นแต่คุณจะย้ายเข้าไปใช้เนื้อหาร่างกายหรือ useCallback
user210757
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.