Галоўная / Артыкулы / Не трэба ўсё час паводлівацца useEffect для кожнай проблемы ў React.

Не трэба ўсё час паводлівацца useEffect для кожнай проблемы ў React.

Эфекты сінхронізуюцца з зовнішнімі системамі; ствараюцца пад рэндэраванні, обрабоцваюцца клікі пад час запуску адбыванняя, запрашваюцца за дапамою інструментаў з рэальнымі дадзеннямі.

1424 слоў

Пастка useEffect

Учэбнікі, які вивучаюць хукі, частаўка ўжываюць useEffect для запрашэння дадзеных, стварэння пахаванага статаусу та адпаведных дзеянняў пад час нажатка на кнопкі. Эфекты сінхронізуюцца з зовнішнімі системамі — яны не ўжываюцца як стандартны інструмент для звычайной логіки атрыбутавання элементаў.

Што на самай працэ практыкуе useEffect

Пасля атрыбутавання элементаў React запускае эфекты, каб прыўязаць слухачы, ачысці цяпі зв’язку чыў імператывна выканаць дзеяння за межамі React. Неправильны ўжытак вызывае проблемы у відобразэнні, двойныя запрашэння дадзеных чыў багі, зв’язаныя з конкурэнцыей.

// ❌ BAD: Two renders, unnecessary state
function UserProfile({ firstName, lastName }) {
  const [fullName, setFullName] = useState('');
useEffect(() => {
    setFullName(`${firstName} ${lastName}`);
  }, [firstName, lastName]);
  return <h1>{fullName}</h1>;
}
// ✅ GOOD: Calculate during render
function UserProfile({ firstName, lastName }) {
  const fullName = `${firstName} ${lastName}`;
return <h1>{fullName}</h1>;
}
// ❌ BAD: Unnecessary effect + state
function ProductList({ products }) {
  const [sortedProducts, setSortedProducts] = useState([]);
useEffect(() => {
    const sorted = [...products].sort((a, b) => a.price - b.price);
    setSortedProducts(sorted);
  }, [products]);
  return (
    <ul>
      {sortedProducts.map(product => (
        <li key={product.id}>{product.name} - ${product.price}</li>
      ))}
    </ul>
  );
}
// ✅ GOOD: Calculate during render (useMemo for expensive ops)
function ProductList({ products }) {
  const sortedProducts = useMemo(() => {
    return [...products].sort((a, b) => a.price - b.price);
  }, [products]);
return (
    <ul>
      {sortedProducts.map(product => (
        <li key={product.id}>{product.name} - ${product.price}</li>
      ))}
    </ul>
  );
}
// ❌ BAD: Manual fetching with useEffect
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
useEffect(() => {
    let cancelled = false;
    async function fetchUser() {
      try {
        setLoading(true);
        const response = await fetch(`/api/users/${userId}`);
        if (!response.ok) throw new Error('Failed to fetch');
        const data = await response.json();
        if (!cancelled) setUser(data);
      } catch (err) {
        if (!cancelled) setError(err.message);
      } finally {
        if (!cancelled) setLoading(false);
      }
    }
    fetchUser();
    return () => { cancelled = true; };
  }, [userId]);
  if (loading) return <Spinner />;
  if (error) return <p>Error: {error}</p>;
  return <div>{user.name}</div>;
}
// ✅ GOOD: Use TanStack Query (React Query)
import { useQuery } from '@tanstack/react-query';
function UserProfile({ userId }) {
  const { data: user, isPending, error } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
  });
  if (isPending) return <Spinner />;
  if (error) return <p>Error loading user</p>;
  return <div>{user.name}</div>;
}
// ❌ BAD: Overcomplicating with useEffect
function LikeButton({ postId }) {
  const [liked, setLiked] = useState(false);
useEffect(() => {
    if (liked) {
      fetch(`/api/posts/${postId}/like`, { method: 'POST' });
    } else {
      fetch(`/api/posts/${postId}/unlike`, { method: 'POST' });
    }
  }, [liked, postId]);
  return (
    <button onClick={() => setLiked(!liked)}>
      {liked ? '❤️' : '🤍'}
    </button>
  );
}
// ✅ GOOD: Handle in the event handler
function LikeButton({ postId }) {
  const [liked, setLiked] = useState(false);
const handleLike = async () => {
    const newLiked = !liked;
    setLiked(newLiked);
    await fetch(`/api/posts/${postId}/${newLiked ? 'like' : 'unlike'}`, {
      method: 'POST',
    });
  };
  return (
    <button onClick={handleLike}>
      {liked ? '❤️' : '🤍'}
    </button>
  );
}
function WindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);

    // Cleanup is crucial!
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  return <p>Window width: {width}px</p>;
}
function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);
useEffect(() => {
    const connection = createWebSocketConnection(roomId);

    connection.onMessage = (msg) => {
      setMessages(prev => [...prev, msg]);
    };
    connection.connect();
    return () => {
      connection.disconnect(); // Cleanup!
    };
  }, [roomId]);
  return <MessageList messages={messages} />;
}
function ChartComponent({ data }) {
  const chartRef = useRef(null);
  const chartInstance = useRef(null);
useEffect(() => {
    if (chartRef.current && !chartInstance.current) {
      chartInstance.current = new ChartLibrary(chartRef.current, {
        type: 'line',
        data: data,
      });
    }
    return () => {
      chartInstance.current?.destroy();
      chartInstance.current = null;
    };
  }, []);
  return <canvas ref={chartRef} />;
}
function PageTitle({ title }) {
  useEffect(() => {
    document.title = `${title} | My App`;

    return () => {
      document.title = 'My App'; // Reset on unmount
    };
  }, [title]);
return null;
}

Чатыры распашчастыя памылкі

1. Вычысленне значэнняў у эфектах

Вырахоўваецца пад час атрыбутавання (або useMemo, калі це дорога процедура). Эфекты, якія зменяють setState на адварыянтах/стане, часта створуюць забраныя ціклі зворачнай связі.

2. Дарогі вырахункі ў эфектах

Чыстыя вырахункі трэба выпрацоўваць пад час атрыбутавання/захавання, а не пасля атрыбутавання элементаў.

3. Зялленне дадзеных як неактуальны эфект

Практыка зяллення дадзеных трэба супакоўваць за дапамогою функцый анулювання, кэшавання і правільнага адпрацоўвання памылак. Лепш выкарыстоваць спецыяльныя бібліятэкі чыстаўкі з механізмамі керавання станом завантажэння; якщо выкарыстоўваюцца эфекты, трэба кераваць процесамі анулювання і супернечкамі.

4. Дзеяння корыстувальца ў эфектах

Функцыі обработкі кліка павінны знаходзіцца ў адпрацоўкі запуску змян. Эфекты, якія запускаюцца пасля змены стану на адварыянтах кліка, створуюць непрямасць і багі.

Дрэва адлічэння

Якщо процедура вырахоўваецца на адварыянтах стану/параметрах → атрыбутавання. Якщо яна рэагуе на клік/надсылку даных → адпрацоўка запуску змян. Якщо яна взаімаецца з API DOM, канектам да сеті чыстаўкі трэціх сторон → эфект.

Калі эфекты ўжоць

Зовнішнія слухальнікі

WebSockets

Ініцыявацыя бібліятэкаў трохі чужых сторон

Імператыўныя API, якія не ўскладненыя React і якія павінны чыста завершваць свою роботу

Заканчэнне

Менш эфектаў, яснейшы прайом дадзеных. Адкладзіце useEffect для сінхронізацыі з системамі за межамі React — не як стандартны інструмент для кожной думкі пасля атрыбутавання.

Режым строгасці два разы запускае эфекты пад час розработы, ўбачваючы недастатковую чыстку — спрыяйце гэтаму пад час налагоджэння сокетаў. Вядомыя бібліятэкі дадзеных лепш паслужаюць для стану сервера, калі компоненты описваюць залежнасці, а не самі ствараюць эфекты фетча. Абмэжыцеся мерамі пры дадаванні эфектаў, якія толькі копіююць атрыбуты ў стан; такі падход часта ёсць неправильным разуменням процесу атрыбутавання.

Функцыі чысткі павінны знішчаць слухальнікі і таймеры; застойныя працоўнікі ёсцю класычным джерелам паведамленняў пра змену стану пасля зняцья компонента.

Масівы залежнасцей ўскладненыя частка кантракту — якщо прыховаць значэння, з’являюцца застарелыя клозуры; якщо занадта многа аб’ектаў і эфектаў у списку, кожная перрадарыце створвае проблемы.

Пад час сінхронізацыі з параметрамі пошуку URL або зовнішніми хранальнямі, лепей выбіраць спецыяльна створаныя хукі, якія інкапсулююць межы эфектаў.

Аплікацыі, якія перрадаруюцца на сервере, не должны запускаць эфекты, якія працуюць толькі ў браузеры, без адпаведных захоўнаў; трэба кераваць імі на адпаведным етапе сістэмы чы роце жыцця аплікацыі.

Функцыі чысткі павінны знімаць слухачы і таймеры; засталелыя обработчыкі є класычным выварам паведамленняў пра зміну стану аплікацыі пасля ўсуненняя з экрана.

Масівы залежнасцей ўскладненыя частка кантракту — якщо прыховаць значэння, з’являюцца застарелыя клозуры; якщо занадта многа аб’ектаў і эфектаў у списку, кожная перрадарыце створвае проблемы.

Пад час сінхронізацыі з параметрамі пошуку URL або зовнішніми хранальнямі, лепей выбіраць спецыяльна створаныя хукі, якія інкапсулююць межы эфектаў.

Програмы, які рэндаруюцца на сервере, не должны запускать эфекты, які працуюць толькі у браузеры, без захоўнікаў; неабходна рэгулювацыя па супакой чы ў цікле жыцця.

Функцыі чысткі павинны знімаць аслухальнікі та таймеры; застойныя обробнікі ўсё чащэ стаюць прычыной апавешчэнняў про зміны стану пасля завершэння рэндараўвання.

Масівы залежнасцяў є частью кантракту — якщо прыховаць значэння, з’являюцца застарелыя функціі; якщо перэлічыць занадта многа об’ектаў та эфектаў, кожны рэндар стане медленым.

Пад час сінхронізацыі з параметрамі пошуку URL чы зовнішніми хранальнямі кращэ вжываць спецыяльныя хукі, якія інкапсулююць межы дзейства.

Програмы, які рэндаруюцца на сервере, не должны запускать эфекты, які працуюць толькі у браузеры, без захоўнікаў; неабходна рэгулювацыя па супакой чы ў цікле жыцця.

Функцыі чысткі павинны знімаць аслухальнікі та таймеры; застойныя обробнікі ўсё чащэ стаюць прычыной апавешчэнняў про зміны стану пасля завершэння рэндараўвання.

Масівы залежнасцей ўскладненыя частка кантракту — якщо прыховаць значэння, з’являюцца застарелыя клозуры; якщо занадта многа аб’ектаў і эфектаў у списку, кожная перрадарыце створае проблемы.

Пад час сінхронізацыі з параметрамі пошуку URL або зовнішніми хранальнямі, лепей выбіраць спецыяльна створаныя хукі, якія інкапсулююць межы дзейнасці эфектаў.

Аплікацыі, якія перрадаруюцца на сервере, не должны запускаць эфекты, якія працуюць толькі ў браузеры, без адпаведных захоўнаў; трэба кераваць імі на адпаведным етапе сістэмнага жыццёвага циклу.

Функціі чысткі павінны знімаць слухачы і таймеры; засталелыя обробнікі ўсё чащэ стаюць прычыной апавешчэнняў про зміны стану пасля завершэння работы аплікацыі.

Масівы залежнасцей ўскладненыя частка кантракту — якщо прыховаць значэння, з’являюцца застарелыя клозуры; якщо занадта многа аб’ектаў і эфектаў у списку, кожная перрадарыце створае проблемы.

Пад час сінхронізацыі з параметрамі пошуку URL або зовнішніми хранальнямі, лепей выбіраць спецыяльна створаныя хукі, якія інкапсулююць межы дзейнасці эфектаў.

Програмы, які рэндаруюцца на сервере, не должны запускать эфекты, які працуюць толькі у браузеры, без захоўнікаў; неабходна рэгулювацыя па супакой чы ў жыцёвым цикле.

Функціі чысткі павинны знішчаць слухачыў і таймеры; застойныя обробнікі є класычным выварам паведамленняў про змену стану пасля зняцья компонента.

Масівы залежнасцяў є частью кантракту — якщо прыховаць значэння, з’являюцца застарелыя клозуры; якщо занадта многа аб’ектаў і эфектаў у масіве, кожны рэндер становіцца вялікай навантажэнням.

Пад час сінхронізацыі з параметрамі пошуку URL чы зовнішніми хранальнямі, краща вжываць спецыяльныя хукі, які інкапсулююць межы дзейства.

Програмы, які рэндаруюцца на сервере, не должны запускать эфекты, які працуюць толькі у браузеры, без захоўнікаў; неабходна рэгулювацыя па супакой чы ў жыцёвым цикле.

Функціі чысткі павинны знішчаць слухачыў і таймеры; застойныя обробнікі є класычным выварам паведамленняў про змену стану пасля зняцья компонента.

Масэвы залежнасцей ўскладненыя частка кантракту — якщо прыховаць значэння, з’являюцца застарелыя клозуры; якщо перапісаць масэв об’ектаў, кожная перрадзіцель стане медленней.

Пры сінхронізацыі з параметрамі пошуку URL або зовнішніми сховішчамі лепей вжываць спецыяльна створаныя хукі, якія інкапсулююць межы вплыву на стан.

Аплікацыі, якія перрадзяюцца на сервере, не должны запускаць эфекты, якія працуюць толькі ў браузеры, без адпаведных захоўнаў; трэба кераваць імі на адзеўнай стадыі жыццёвага циклу.

Функціі чысткі павінны абрываць слухачы та таймеры; засталія обработчыкі є класычным выкарыстоўваннем паведамленняў про зміну стану пасля завершэння работы компонента.