React新手开发者求助:useEffect空依赖数组的最佳实践及缺失依赖警告解决
Hey there! Let's tackle your questions one by one—first, whether empty dependency arrays are bad practice, then how to fix that warning in your code.
Is an empty useEffect dependency array a bad practice?
It’s not always bad, but it’s often a red flag. An empty array ([]) tells React your effect should only run once (when the component mounts) and never re-run, even if values used inside the effect change.
Training courses use empty arrays for simple examples (like fetching initial data without any dynamic parameters), but in real apps, this can lead to stale closures—where your effect uses old versions of variables/functions because it never re-runs when those values update. The warning React gives you is trying to prevent that!
Why are you seeing the missing dependency warning?
Your useEffect calls refreshDisplay, but refreshDisplay depends on other functions (getAllShopOpinions, loadDisplayedShopOpinions) and state setters (setDisplayedShopOpinions). React needs to know about these dependencies to ensure your effect runs with the latest values. If you ignore the warning, you might end up with bugs (like fetching old data or state not updating correctly).
Fixing your code step by step
Let’s go through the key changes needed:
1. Move useStyles outside your component
Defining useStyles inside the component creates a new function every render, which is unnecessary. Move it to the top:
const useStyles = makeStyles((theme) => ({ deleteBtn:{ color:"#cc0e00" } })); const AdminShopOpinions = (props) => { // ... rest of your code const classes = useStyles(); }
2. Optimize loadDisplayedShopOpinions to avoid repeated state updates
Right now, you’re calling setDisplayedShopOpinions in a loop, which triggers multiple re-renders. Instead, build the entire array of JSX elements first, then update state once:
const loadDisplayedShopOpinions = (shopOpinions) => { const opinionsElements = shopOpinions.map(opinion => ( <article key={opinion.id} className="shop-opinion-item"> <section className="shop-opinion-infos"> <p>" {opinion.comment} "</p> <p>Auteur : <em>{opinion.first_name} {opinion.last_name}</em></p> <p><em>le {convertDate(opinion.creation_timestamp)}</em></p> </section> {opinion.pict_url && ( <section className="shop-opinion-pict"> <img src={config.shop_opinion_pict_url + opinion.pict_url} alt={`illustration${opinion.id}`}/> </section> )} <Button className={classes.deleteBtn} onClick={() => deleteDisplayedOpinion(opinion.id)} > <DeleteIcon /> </Button> </article> )); setDisplayedShopOpinions(opinionsElements); }
3. Use useCallback for functions used in useEffect
Wrap refreshDisplay with useCallback to stabilize its reference (so it doesn’t change every render unless its dependencies do). Then add it to your useEffect dependency array:
const refreshDisplay = useCallback(() => { getAllShopOpinions() .then(shopOpinionsDB => { loadDisplayedShopOpinions(shopOpinionsDB); }) }, [getAllShopOpinions, loadDisplayedShopOpinions]); // Now your useEffect can safely include refreshDisplay in dependencies useEffect(() => { refreshDisplay(); }, [refreshDisplay]);
4. Optional: Stabilize deleteDisplayedOpinion too
While not required for the current warning, wrapping this function with useCallback avoids potential issues if you ever use it in an effect or pass it to child components:
const deleteDisplayedOpinion = useCallback((id) => { deleteOpinion(id) .then(() => { refreshDisplay(); }) }, [deleteOpinion, refreshDisplay]);
Full modified code
Here’s how your code looks with all these fixes:
import React, {useState, useEffect, useCallback} from "react" import {getAllShopOpinions, deleteOpinion} from "../../api/shopOpinions" import {convertDate} from "../../utils/utils" import {config} from "../../config/config" //Imports des composants de l'UI externes import Header from "../headers/header" import HeaderPages from "../headers/headerPages" import Footer from "../footer" import AdminMenu from "../../components/adminMenu" //Imports depuis la librairie MaterialUI import { makeStyles } from "@material-ui/styles" import { Button } from '@material-ui/core' import DeleteIcon from '@material-ui/icons/Delete' // Move useStyles outside the component const useStyles = makeStyles((theme) => ({ deleteBtn:{ color:"#cc0e00" } })); const AdminShopOpinions = (props) => { const headerTitle ="Administration" const headerBreadcrumbs = [{value: "Accueil", link:"/"},{value: "Administration", link:"/admin"},{value: "Avis", link:null}] const [displayedShopOpinions, setDisplayedShopOpinions] = useState([]) const classes = useStyles(); // Optimized load function with single state update const loadDisplayedShopOpinions = useCallback((shopOpinions) => { const opinionsElements = shopOpinions.map(opinion => ( <article key={opinion.id} className="shop-opinion-item"> <section className="shop-opinion-infos"> <p>" {opinion.comment} "</p> <p>Auteur : <em>{opinion.first_name} {opinion.last_name}</em></p> <p><em>le {convertDate(opinion.creation_timestamp)}</em></p> </section> {opinion.pict_url && ( <section className="shop-opinion-pict"> <img src={config.shop_opinion_pict_url + opinion.pict_url} alt={`illustration${opinion.id}`}/> </section> )} <Button className={classes.deleteBtn} onClick={() => deleteDisplayedOpinion(opinion.id)} > <DeleteIcon /> </Button> </article> )); setDisplayedShopOpinions(opinionsElements); }, [classes]); // Stabilized refresh function const refreshDisplay = useCallback(() => { getAllShopOpinions() .then(shopOpinionsDB => { loadDisplayedShopOpinions(shopOpinionsDB); }) }, [getAllShopOpinions, loadDisplayedShopOpinions]); // Stabilized delete function const deleteDisplayedOpinion = useCallback((id) => { deleteOpinion(id) .then(() => { refreshDisplay(); }) }, [deleteOpinion, refreshDisplay]); // useEffect with proper dependencies useEffect(() => { refreshDisplay() }, [refreshDisplay]) // Fonction d'affichage des avis const showDisplayedShopOpinions = () => { return( <section className="shop-opinions-container"> {displayedShopOpinions.length > 0 ? displayedShopOpinions : <p>Aucun avis à afficher</p>} </section> ) } return ( <section className="root"> <Header /> <HeaderPages headerTitle={headerTitle} headerBreadcrumbs={headerBreadcrumbs}/> <section className="admin-container"> <AdminMenu /> <section className="admin-content"> <h4>Gestion des avis</h4> {showDisplayedShopOpinions()} </section> </section> <Footer /> </section> ) } export default AdminShopOpinions
Final notes
- Empty dependency arrays are okay for one-time setup (like adding an event listener that doesn’t depend on component state/props), but always double-check if your effect uses any values that might change.
- React’s dependency warnings are there to help you avoid subtle bugs—ignoring them can lead to hard-to-debug issues later on.
内容的提问来源于stack exchange,提问作者Tholhen

