Як використовувати компонентWillMount () в React Hooks?


175

В офіційних документах React це згадується -

Якщо ви знайомі з методами життєвого циклу класу React, ви можете подумати про використанняEffect Hook як компонентDidMount, компонентDidUpdate та компонентWillUnmount.

Моє запитання - як ми можемо використовувати componentWillMount()метод lifecyle в гачку?

Відповіді:


337

Ви не можете використовувати будь-якого з існуючих методів життєвого циклу ( componentDidMount, componentDidUpdate, і componentWillUnmountт.д.) в гаку. Їх можна використовувати лише в класних компонентах. А з гачками можна використовувати лише функціональні компоненти. Рядок нижче походить від документа React:

Якщо ви знайомі з React методи життєвого циклу класу, ви можете думати про useEffectгачку componentDidMount, componentDidUpdateі в componentWillUnmountпоєднанні.

Ви можете імітувати цей метод життєвого циклу з класового компонента у функціональні компоненти.

Код всередині componentDidMountзапустіть один раз, коли компонент змонтований. useEffectгак еквівалент такої поведінки

useEffect(() => {
  // Your code here
}, []);

Зауважте тут другий параметр (порожній масив). Це запуститься лише один раз.

Без другого параметраuseEffect гачок буде викликатися кожен рендер компонент , який може бути небезпечним.

useEffect(() => {
  // Your code here
});

componentWillUnmountвикористовується для очищення (наприклад, видалення слухачів подій, скасування таймера тощо). Скажімо, ви додаєте в нього слухача події componentDidMountта видаляєте його, componentWillUnmountяк показано нижче.

componentDidMount() {
  window.addEventListener('mousemove', () => {})
}

componentWillUnmount() {
  window.removeEventListener('mousemove', () => {})
}

Еквівалент гака вищевказаного коду буде наступним

useEffect(() => {
  window.addEventListener('mousemove', () => {});

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])

183
Приємне пояснення для інших подій життєвого циклу, але це не питання конкретно про альтернативу компоненту компонентWillMount ().
Шираз

3
У прикладах PanResponder я бачив, що компонентWillMount здається необхідним, інакше ви отримаєте невизначені panHandlers.
Dror Bar

2
Тепер я дуже розумію useEffect()функцію, дякую.
Iharob Al Asimi

67
Чому це прийнята відповідь ?? Ви не згадали про еквівалент гакаcomponentWillMount
Mykybo

3
@techexpert Питання задається еквівалентом componentWillMount, ні componentWillUnmount. Ця відповідь справді зовсім не відповідає на запитання, а лише повторює те, що вже мали на увазі ОП.
JoshuaCWebDeveloper

62

useComponentDidMount гачок

У більшості випадків useComponentDidMountце інструмент для використання. Він запуститься лише один раз після встановлення компонента (початкове візуалізація).

 const useComponentDidMount = func => useEffect(func, []);

useComponentWillMount

Важливо зазначити, що в класі компоненти componentWillMountвважаються застарілими. Якщо вам потрібен код для запуску лише один раз, перш ніж компонент змонтується, ви можете використовувати конструктор. Більше про це тут . Оскільки функціональний компонент не має еквівалентного конструктора, використання гачка для запуску коду лише один раз, перш ніж компоненти компонентів можуть мати сенс у певних випадках. Ви можете досягти цього за допомогою спеціального гачка.

const useComponentWillMount = func => {
  const willMount = useRef(true);

  if (willMount.current) {
    func();
  }

  willMount.current = false;
};

Однак є і підводний камінь. Не використовуйте його для асинхронного встановлення свого стану (наприклад, після запиту сервера. Так як ви можете очікувати, що це вплине на початкове відображення, яке не буде). Такі справи мають бути вирішені useComponentDidMount.

Демо

const Component = (props) => {
  useComponentWillMount(() => console.log("Runs only once before component mounts"));
  useComponentDidMount(() => console.log("Runs only once after component mounts"));
  ...

  return (
    <div>{...}</div>
  );
}

Повна демонстрація


18
Це єдина відповідь, яка відповідає на питання і має сенс. Дякую!
chumakoff

3
Єдина проблема з цим полягає в тому, що ви отримуєте додатковий візуалізацію через оновлення штату. Використовуючи замість цього ref, ви отримуєте бажану поведінку без додаткового візуалізації: `const useComponentWillMount = func => {const willMount = useRef (вірно); useEffect (() => {willMount.current = false;}, []); if (willMount.current) {func (); }}; `
remix23

2
Ця функціональна реалізація на componentWillMountоснові useEffectмає дві проблеми. Перший - це те, що в функціональних компонентах немає монтажного життєвого циклу, обидва гаки запускаються після того, як компонент буде наданий, тому Runs only once before component mountsвводиться в оману. Другий - це те, що componentWillMountвикликається при візуалізації сервера, а useEffectне є. Багато бібліотек все ще покладаються на це, UNSAFE_componentWillMountоскільки в даний час це єдиний спосіб запустити сервер на сторону.
Паоло Моретті

2
@PaoloMoretti, спасибі Цей компонентWillMount гак, не є точним еквівалентом життєвого циклу компонента WillMount в компоненті класу. Однак функція, передана йому, запуститься негайно, лише в перший раз, коли вона буде викликана. Це практично означає, що він буде запущений до того, як він буде наданий, і перш ніж він навіть поверне значення вперше. Чи можемо ми з цим погодитися? Я погоджуюся, що використання іменного компонентаWillMount не є ідеальним, оскільки це ім'я несе певний сенс у версії життєвого циклу класу. Можливо, я краще називаю це "useRunPreMount".
Бен Карп

1
@PaoloMoretti, я не дуже розумію. Я не працюю з SSR, але я розумію, що для SSR компонентWillMount працює двічі - один раз на сервері та один раз на клієнті. Я думаю, те саме стосується зворотного виклику, який передається до useComponentDidMount. useComponentDidMount реле на useEffect для припинення виклику зворотного дзвінка. Доки не буде виконано зворотний виклик useEffect, функція компонента буде виконуватися двічі - один раз на сервері та один раз на клієнті. Чи не так?
Бен Карп

53

За інформацією reactjs.org, компонентWillMount надалі не підтримуватиметься. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

Немає необхідності використовувати компонентWillMount.

Якщо ви хочете щось зробити перед монтуванням компонента, просто зробіть це в конструкторі ().

Якщо ви хочете робити мережеві запити, не робіть цього в компонентWillMount. Це тому, що це призведе до несподіваних помилок.

Мережеві запити можна виконувати в компонентDidMount.

Сподіваюся, це допомагає.


оновлено 03.03.2019

Причина, чому ви запитуєте компонентWillMount, ймовірно, полягає в тому, що ви хочете ініціалізувати стан перед рендерінгами.

Просто зробіть це в useState.

const helloWorld=()=>{
    const [value,setValue]=useState(0) //initialize your state here
    return <p>{value}</p>
}
export default helloWorld;

або, можливо, Ви хочете запустити функцію в компонентWillMount, наприклад, якщо ваш початковий код виглядає так:

componentWillMount(){
  console.log('componentWillMount')
}

за допомогою гачка, все, що вам потрібно зробити, - це видалити спосіб життєвого циклу:

const hookComponent=()=>{
    console.log('componentWillMount')
    return <p>you have transfered componeWillMount from class component into hook </p>
}

Я просто хочу додати щось до першої відповіді про useEffect.

useEffect(()=>{})

useEffect працює на кожному візуалізації, це комбінація компонентівDidUpdate, компонентDidMount та ComponentWillUnmount.

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

Якщо ми додамо порожній масив у useEffect, він запускається саме тоді, коли компонент встановлений. Це тому, що UseEffect порівняє масив, який ви передали йому. Таким чином, він не повинен бути порожнім масивом. Це може бути масив, який не змінюється. Наприклад, це може бути [1,2,3] або ['1,2']. useEffect все ще працює лише при встановленні компонента.

Від вас залежить, чи хочете ви, щоб він запускався лише один раз або працює після кожного візуалізації. Це не небезпечно, якщо ви забули додати масив, поки ви знаєте, що робите.

Я створив зразок для гачка. Будь ласка, перевірте це.

https://codesandbox.io/s/kw6xj153wr


оновлено 21.08.2019

Це було білим з моменту написання вищезгаданої відповіді. Я думаю, що на це потрібно звернути увагу. Коли ви використовуєте

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

Коли react порівнює значення, які ви передали масиву [], він використовує Object.is () для порівняння. Якщо ви передаєте йому об’єкт, наприклад

useEffect(()=>{},[{name:'Tom'}])

Це точно те саме, що:

useEffect(()=>{})

Він буде повторно відображатися кожен раз, оскільки коли Object.is () порівнює об'єкт, він порівнює його посилання, а не саме значення. Це те саме, що чому {} === {} повертає помилкові, оскільки їх посилання різні. Якщо ви все ще хочете порівнювати сам об'єкт не з посиланням, ви можете зробити щось подібне:

useEffect(()=>{},[JSON.stringify({name:'Tom'})])

17
і питання полягало в тому, як реалізувати це за допомогою гачків
Шубхам Хатрі

3
але вам не потрібно реалізовувати його за допомогою гачків, оскільки він не буде підтримуватися. Не потрібно вчитися робити це за допомогою гачків.
MING WU

1
Тепер, коли ви згадали, що компонентDidMount - це правильний життєвий цикл, який ви можете використовувати, ви могли б додати, як це реалізувати у своїй відповіді, і тоді ваша відповідь матиме більше сенсу, ніж прийнята відповідь
Shubham Khatri

8
Звичайно, це має бути прийнята відповідь - це пояснює, що ComponentWillMount недоступний у парадигмі гачків. Ініціалізація функціональних компонентів спрощена - вона просто повинна бути частиною функції
Шираз

1
Як це те саме, що і компонентWillMount? Якщо ви кинете код у функціональний компонент, він запускатиме кожне візуалізацію, не тільки тоді, коли компонент збирається змонтувати.
Перезарядка

13

useLayoutEffectможе досягти цього з порожнім набором спостерігачів ( []), якщо функціональність насправді схожа наcomponentWillMount - вона запуститься до того, як перший вміст потрапить до DOM - хоча насправді є два оновлення, але вони є синхронними перед виведенням на екран.

наприклад:


function MyComponent({ ...andItsProps }) {
     useLayoutEffect(()=> {
          console.log('I am about to render!');
     },[]);

     return (<div>some content</div>);
}

Перевага над useStateініціалізатором / сеттером або useEffect, хоча вона може обчислити пропуск рендерінгу, фактичних повторних рендерингу в DOM немає, що користувач помітить, і він запускається перед першим помітним візуалізацією, що не стосуєтьсяuseEffect . Зворотний бік - це, звичайно, невелика затримка вашого першого візуалізації, оскільки перевірка / оновлення має відбутися перед фарбуванням на екран. Це дійсно залежить від вашого випадку використання.

Я думаю, що особисто useMemoце добре в деяких нішевих випадках, коли вам потрібно зробити щось важке - доки ви пам’ятаєте, що це виняток проти норми.


3
useLayoutEffect - це шлях !!!! Ця відповідь на моє запитання щодо перевірки того, чи користувач увійшов. (Проблема полягала в тому, що компоненти завантажуватимуться, а потім перевіряйте, чи користувач увійшов.) Моє питання, чи це стандартна практика? Я не бачу надто багато місць
Джессіка

1
так, це досить часто; згадується і в офіційних документах React - лише в меншому тексті через розгалуження подвійного рендерингу DOM, щоб використовувати логіку перед тим, як користувач помітить.
rob2d

Насправді він працює після надання компонента. Отже, це зовсім інше, ніж компонентWillMount.
Jiri Mihal

7

Ось так я моделюю конструктор у функціональних компонентах за допомогою useRefгачка:

function Component(props) {
    const willMount = useRef(true);
    if (willMount.current) {
        console.log('This runs only once before rendering the component.');
        willMount.current = false;        
    }

    return (<h1>Meow world!</h1>);
}

Ось приклад життєвого циклу:

function RenderLog(props) {
    console.log('Render log: ' + props.children);
    return (<>{props.children}</>);
}

function Component(props) {

    console.log('Body');
    const [count, setCount] = useState(0);
    const willMount = useRef(true);

    if (willMount.current) {
        console.log('First time load (it runs only once)');
        setCount(2);
        willMount.current = false;
    } else {
        console.log('Repeated load');
    }

    useEffect(() => {
        console.log('Component did mount (it runs only once)');
        return () => console.log('Component will unmount');
    }, []);

    useEffect(() => {
        console.log('Component did update');
    });

    useEffect(() => {
        console.log('Component will receive props');
    }, [count]);


    return (
        <>
        <h1>{count}</h1>
        <RenderLog>{count}</RenderLog>
        </>
    );
}
[Log] Body
[Log] First time load (it runs only once)
[Log] Body
[Log] Repeated load
[Log] Render log: 2
[Log] Component did mount (it runs only once)
[Log] Component did update
[Log] Component will receive props

Звичайно, компоненти класу не мають Bodyкроків, неможливо зробити імітацію 1: 1 через різні концепції функцій та класів.


Я не занурився у ваш приклад, але ваш перший фрагмент коду працює для мене, дякую!
САндрій

6

Я написав спеціальний гачок, який запустить функцію один раз перед першим візуалізацією.

useBeforeFirstRender.js

import { useState, useEffect } from 'react'

export default (fun) => {
  const [hasRendered, setHasRendered] = useState(false)

  useEffect(() => setHasRendered(true), [hasRendered])

  if (!hasRendered) {
    fun()
  }
}

Використання:

import React, { useEffect } from 'react'
import useBeforeFirstRender from '../hooks/useBeforeFirstRender'


export default () => { 
  useBeforeFirstRender(() => {
    console.log('Do stuff here')
  })

  return (
    <div>
      My component
    </div>
  )
}

3

Існує приємний спосіб вирішити componentDidMountі componentWillUnmountз ним useEffect.

Виходячи з документації, useEffectможна повернути функцію "очищення". ця функція не буде використовуватися під час першого useEffectдзвінка, лише при наступних дзвінках.

Тому, якщо ми будемо використовувати useEffectгачок взагалі без залежностей, гачок буде викликатися лише тоді, коли компонент встановлений, а функція "очищення" викликається, коли компонент відключений.

useEffect(() => {
    console.log('componentDidMount');

    return () => {
        console.log('componentWillUnmount');
    };
}, []);

Виклик функції повернення очищення викликається лише тоді, коли компонент відключений.

Сподіваюся, це допомагає.


2
Як це допомагає, якщо це не має нічого спільного з компонентWillMount ? Я щось пропускаю?
ZenVentzi

Так, ви пропускаєте той факт, що за той самий useEffectдзвінок ви отримуєте однаковий функціонал componentWillMountі componentWillUnmountв приємному та чистому вигляді
AfikDeri

Це неправда, useEffectзапускається лише після візуалізації, а componentWillMountзапускається до того, як компонент виводить.
Перезарядка

@Overcode, про який я говорив, componentDidMountне говорив componentWillMount. Я пропустив це у питанні, моє погано.
AfikDeri

1

Ви можете зламати гачок useMemo, щоб імітувати подію життєвого циклу компонента WillMount. Просто зробіть:

const Component = () => {
   useMemo(() => {
     // componentWillMount events
   },[]);
   useEffect(() => {
     // componentWillMount events
     return () => {
       // componentWillUnmount events
     }
   }, []);
};

Вам потрібно буде тримати гачок useMemo перед тим, що взаємодіє з вашою державою. Це не так, як це задумано, але воно працювало на мене для всіх проблем компонента WillMount.

Це працює, тому що useMemo не вимагає фактичного повернення значення, і вам не потрібно використовувати його як будь-що, але оскільки воно запам'ятовує значення, засноване на залежностях, яке запускатиметься лише один раз ("[]") і його поверх нашого компонента запускається один раз, коли компонент монтується раніше всього.


0

https://reactjs.org/docs/hooks-reference.html#usememo

Пам'ятайте, що функція, передана для використанняMemo, виконується під час надання. Не робіть нічого, чого зазвичай не робили під час візуалізації. Наприклад, побічні ефекти належать до використанняEffect, а не до використанняMemo.


usememo призначений для оптимізації продуктивності. Гак буде винесений знову після того, як він уже встановлений, якщо зміна опори, яка переможе ціль автора.
max54

0

Відповідь Бен Карпа здається мені єдиною справедливою.

Але оскільки ми використовуємо функціональні способи, від закриття та HoC може бути корисний інший підхід:

const InjectWillmount = function(Node, willMountCallback) {
  let isCalled = true;
  return function() {
    if (isCalled) {
      willMountCallback();
      isCalled = false;
    }
    return Node;
  };
};

Потім використовуйте його:

const YourNewComponent = InjectWillmount(<YourComponent />, () => {
  console.log("your pre-mount logic here");
});

0

Коротка відповідь на ваше первісне запитання, як componentWillMountможна користуватися React Hooks:

componentWillMountє застарілим і вважається спадщиною . Реагувати рекомендації :

Як правило, ми рекомендуємо використовувати конструктор () замість цього для ініціалізації стану.

Тепер у FAQ Hook ви дізнаєтесь, що таке еквівалент конструктора класів для функціональних компонентів:

конструктор: Функціональні компоненти не потребують конструктора. Ви можете ініціалізувати стан у виклику useState. Якщо обчислення початкового стану дороге, ви можете передати функцію useState.

Отже, приклад використання componentWillMountвиглядає так:

const MyComp = () => {
  const [state, setState] = useState(42) // set initial value directly in useState 
  const [state2, setState2] = useState(createInitVal) // call complex computation

  return <div>{state},{state2}</div>
};

const createInitVal = () => { /* ... complex computation or other logic */ return 42; };

0

Просто додайте порожній масив залежностей у useEffect, він буде працювати componentDidMount.

useEffect(() => {
  // Your code here
  console.log("componentDidMount")
}, []);

0

Існує простий трюк для імітації componentDidMountта componentWillUnmountза допомогою useEffect:

useEffect(() => {
  console.log("componentDidMount");

  return () => {
    console.log("componentWillUnmount");
  };
}, []);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.