You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React新手开发者求助:useEffect空依赖数组的最佳实践及缺失依赖警告解决

Understanding useEffect Empty Dependency Warnings & Fixing Your Code

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 09:07:34