Я использую несколько хуков useEffect для выполнения функций componentDidMount и componentDidUpdate, однако, похоже, когда компонент загружается, все мои useEffect срабатывают изначально ...

const testComp = () => {
   const stateUserId = useSelector(state => { return state.userId; });
   const [userId, setUserId] = useState(stateUserId);
   const [active, setActive] = useState(false);
   const [accountId, setAccountId] = useState();
   
   useEffect(() => {
      console.log('component did mount...');
   }, [userId]);
   
   useEffect(() => {
      console.log('should trigger when userId changes...');
   }, [userId]);

   useEffect(() => {
      console.log('should trigger when active changes...');
   }, [active]);
   
   useEffect(() => {
      console.log('should trigger when accountId changes...');
   }, [accounted]);
  
   return (<div>...</div);
}

Когда мой компонент монтируется, я вижу там весь журнал консоли

component did mount...
should trigger when userId changes...
should trigger when active changes...
should trigger when accountId changes...

Как я мог позволить срабатывать только первому useEffect, а остальные три срабатывать только при изменении зависимости?

1
KevDing 1 Авг 2020 в 06:16

3 ответа

Лучший ответ

useEffect не является прямой заменой componentDidMount и componentDidUpdate. Эффект будет запускаться после каждого рендеринга, поэтому вы видите все эти журналы консоли. Согласно документации React, второй параметр useEffect означает

вы можете активировать их (эффекты) только при изменении определенных значений.

После первоначального рендеринга, если компонент рендерится снова, срабатывают только эффекты, соответствующие изменениям значений.

Один из способов добиться желаемого - создать дополнительные переменные для хранения начальных значений и выполнять сравнения в useEffect, когда это необходимо.

const testComp = () => {
  const initUserId =  useSelector(state => { return state.userId; });
  const stateUserId = initUserId;
   const [userId, setUserId] = useState(stateUserId);
   
   useEffect(() => {
      console.log('component did mount...');
   }, [userId]);
   
   useEffect(() => {
      if (userId !== initUserId) {
        console.log('should trigger when userId changes...');
      }
      
   }, [userId]);
  
   return <div>...</div>
}
2
Andrew Zheng 1 Авг 2020 в 03:48

Любой обработчик useEffect всегда будет активирован при монтировании компонента и при изменении некоторых переменных в его массиве зависимостей. Если вы хотите выполнить действие только тогда, когда переменная изменится, вы должны сначала проверить входящее значение и выполнить необходимые проверки. Также ваш первый useEffect должен иметь пустой массив зависимостей, чтобы запускать его только при монтировании компонента, потому что он также будет вызываться при изменении userId.

1
MaCadiz 1 Авг 2020 в 03:25

Вы можете использовать индивидуальный useEffect:

const useCustomEffect = (func, deps) => {
    const didMount = useRef(false);

    useEffect(() => {
        if (didMount.current) func();
        else didMount.current = true;
    }, deps);
}

И используйте его в своем компоненте:


useCustomEffect(() => {
      console.log('trigger when userId changes...');
}, [userId]);

1
Mohsen Niknam Pirzadeh 1 Авг 2020 в 03:50