Como usar componentWillMount () nos ganchos de reação?

176

Nos documentos oficiais do React, ele menciona -

Se você estiver familiarizado com os métodos do ciclo de vida da classe React, considere usar o Hook Effect como componentDidMount, componentDidUpdate e componentWillUnmount combinados.

Minha pergunta é - como podemos usar o componentWillMount()método de ciclo de vida em um gancho?

Abrar
fonte

Respostas:

340

Você não pode usar qualquer um dos métodos de ciclo de vida existentes ( componentDidMount, componentDidUpdate, componentWillUnmountetc.) em um gancho. Eles só podem ser usados ​​em componentes de classe. E com ganchos você pode usar apenas em componentes funcionais. A linha abaixo vem do documento React:

Se você estiver familiarizado com Reagir métodos de ciclo de vida de classe, você pode pensar em useEffectgancho como componentDidMount, componentDidUpdatee componentWillUnmountcombinado.

A sugestão é que você possa imitar esse método do ciclo de vida do componente de classe em um componente funcional.

O código interno é componentDidMountexecutado uma vez quando o componente está montado. useEffectgancho equivalente para esse comportamento é

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

Observe o segundo parâmetro aqui (matriz vazia). Isso será executado apenas uma vez.

Sem o segundo parâmetro, o useEffectgancho será chamado em todas as renderizações do componente que possam ser perigosas.

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

componentWillUnmounté usado para limpeza (como remover ouvintes de eventos, cancelar o cronômetro etc.). Digamos que você esteja adicionando um ouvinte de evento componentDidMounte removendo-o componentWillUnmountcomo abaixo.

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

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

O equivalente do código acima do gancho será o seguinte

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

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])
Bhaskar Gyan Vardhan
fonte
184
Boa explicação para outros eventos do ciclo de vida, mas essa não é a pergunta específica sobre uma alternativa ao componentWillMount ().
Shiraz
3
Nos exemplos do PanResponder, vi componentWillMount parecer necessário, caso contrário, você obtém panHandlers indefinidos.
Dror Bar
2
Agora eu realmente entendo a useEffect()função, obrigado.
Iharob Al Asimi
67
Por que essa é uma resposta aceita? Você não mencionou um equivalente em gancho paracomponentWillMount
Mykybo 04/11/19
3
@techexpert A pergunta pedia um equivalente a componentWillMount, não componentWillUnmount. Essa resposta realmente não responde à pergunta e apenas reitera o que o OP já implicava em saber.
JoshuaCWebDeveloper
62

gancho useComponentDidMount

Na maioria dos casos, useComponentDidMounté a ferramenta a ser usada. Ele será executado apenas uma vez, após a montagem do componente (renderização inicial).

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

useComponentWillMount

É importante observar que nos componentes da classe componentWillMounté considerado legado. Se você precisar que o código seja executado apenas uma vez antes da montagem do componente, você poderá usar o construtor. Mais sobre isso aqui . Como o componente funcional não tem o equivalente de um construtor, usar um gancho para executar o código apenas uma vez antes da montagem do componente pode fazer sentido em certos casos. Você pode conseguir isso com um gancho personalizado.

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

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

  willMount.current = false;
};

No entanto, existe uma armadilha. Não use-o para definir seu estado de forma assíncrona (ex., Após uma solicitação do servidor. Como você pode esperar, isso afeta a renderização inicial, o que não ocorrerá). Tais casos devem ser tratados com useComponentDidMount.

Demo

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

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

Demonstração completa

Ben Carp
fonte
18
Esta é a única resposta que responde à pergunta e faz sentido. Obrigado!
21719 chumakoff
3
O único problema é que você obtém uma renderização extra devido à atualização do estado envolvida. Ao usar uma ref, você obtém o comportamento desejado sem a renderização extra: `const useComponentWillMount = func => {const willMount = useRef (true); useEffect (() => {willMount.current = false;}, []); if (willMount.current) {func (); }}; `
remix23
2
Essa implementação funcional do componentWillMountbased on useEffecttem dois problemas. A primeira é que não há um ciclo de vida de montagem nos componentes funcionais, os dois ganchos serão executados após a renderização do componente, o que Runs only once before component mountsé enganoso. O segundo é que componentWillMounté chamado na renderização do servidor e useEffectnão é. Muitas bibliotecas ainda dependem, UNSAFE_componentWillMountporque atualmente é a única maneira de acionar um efeito colateral no servidor.
Paolo Moretti
2
@PaoloMoretti, obrigado. Esse gancho componentWillMount não é o equivalente exato do ciclo de vida componentWillMount em um componente de classe. No entanto, a função que é passada a ele será executada imediatamente, apenas na primeira vez em que for chamada. Na prática, isso significa que será executado antes de ser renderizado e antes mesmo de retornar um valor pela primeira vez. Podemos concordar com isso? Concordo que o uso do nome componentWillMount não é ideal, pois esse nome carrega certo significado da versão do ciclo de vida da classe. Talvez seja melhor chamá-lo de "useRunPreMount".
Ben Carp
1
@PaoloMoretti, eu não entendo direito. Não trabalho com SSR, mas meu entendimento é que, no SSR componentWillMount, é executado duas vezes - uma vez no servidor e outra no cliente. Eu acho que o mesmo é verdade para o retorno de chamada que é passado para useComponentDidMount. useComponentDidMount repassa useEffect para parar de invocar o retorno de chamada. Até que o retorno de chamada do useEffect seja executado, a função do componente será executada duas vezes - uma vez no servidor e outra no cliente. Não é esse o caso?
Ben Carp
53

De acordo com reactjs.org, componentWillMount não será suportado no futuro. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

Não há necessidade de usar componentWillMount.

Se você quiser fazer algo antes da montagem do componente, faça-o no construtor ().

Se você deseja fazer solicitações de rede, não faça em componentWillMount. É porque isso levará a erros inesperados.

Solicitações de rede podem ser feitas em componentDidMount.

Espero que ajude.


atualizado em 03/03/2019

O motivo pelo qual você solicita componentWillMount é provavelmente porque deseja inicializar o estado antes da renderização.

Basta fazê-lo em useState.

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

ou talvez Você queira executar uma função no componentWillMount, por exemplo, se o seu código original estiver assim:

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

com hook, tudo que você precisa fazer é remover o método do ciclo de vida:

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

Eu só quero adicionar algo à primeira resposta sobre useEffect.

useEffect(()=>{})

useEffect é executado em cada renderização, é uma combinação de componentDidUpdate, componentDidMount e ComponentWillUnmount.

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

Se adicionarmos uma matriz vazia em useEffect, ela será executada exatamente quando o componente estiver montado. É porque useEffect irá comparar a matriz que você passou para ela. Portanto, ele não precisa ser um array vazio. Pode ser um array que não está sendo alterado. Por exemplo, pode ser [1,2,3] ou ['1,2']. useEffect ainda é executado somente quando o componente é montado.

Depende de você se você deseja executar apenas uma vez ou após cada renderização. Não é perigoso se você esqueceu de adicionar uma matriz, desde que saiba o que está fazendo.

Eu criei uma amostra para gancho. Por favor, confira.

https://codesandbox.io/s/kw6xj153wr


atualizado em 21/08/2019

Está branco desde que escrevi a resposta acima. Acho que há algo que você precisa prestar atenção. Quando você usa

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

Quando reagir compara os valores que você passou para a matriz [], ela usa Object.is () para comparar. Se você passar um objeto para ele, como

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

É exatamente o mesmo que:

useEffect(()=>{})

Ele será renderizado novamente sempre que, quando Object.is () compara um objeto, compara sua referência e não o valor em si. É o mesmo porque {} === {} retorna false porque suas referências são diferentes. Se você ainda deseja comparar o objeto em si, não a referência, pode fazer algo assim:

useEffect(()=>{},[JSON.stringify({name:'Tom'})])
MING WU
fonte
17
e a pergunta era como implementá-lo com ganchos
Shubham Khatri
3
mas você não precisa implementá-lo com ganchos porque não será suportado. Não há necessidade de aprender como fazer isso com ganchos.
MING WU
1
Agora que você mencionou que componentDidMount é o ciclo de vida correta de uso, você poderia ter acrescentado como implementar isso em sua resposta e, em seguida, sua resposta faria mais sentido do que a resposta aceita
Shubham Khatri
8
Certamente, essa deve ser a resposta aceita - explica que o ComponentWillMount não está disponível no paradigma dos ganchos. Inicialização em componentes funcionais é simplificada - ele só precisa ser parte da função
Shiraz
1
Como isso é a mesma coisa que componentWillMount? Se você inserir o código no componente funcional, ele executará todas as renderizações, não apenas quando o componente estiver prestes a ser montado.
Overcode
13

useLayoutEffectpoderia fazer isso com um conjunto vazio de observadores ( []) se a funcionalidade for realmente semelhante a componentWillMount- ela será executada antes que o primeiro conteúdo chegue ao DOM - embora existam duas atualizações, mas elas sejam síncronas antes de serem exibidas na tela.

por exemplo:


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

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

O benefício finalizado useStatecom um inicializador / setter ou, useEffectembora possa computar um passe de renderização, não há renderizações reais no DOM que um usuário notará e é executado antes da primeira renderização perceptível, o que não é o caso de useEffect. A desvantagem é, obviamente, um pequeno atraso na sua primeira renderização, uma vez que uma verificação / atualização deve ocorrer antes da pintura na tela. Realmente depende do seu caso de uso.

Eu acho que pessoalmente, useMemoé bom em alguns casos de nicho em que você precisa fazer algo pesado - desde que você tenha em mente que é a exceção versus a norma.

rob2d
fonte
3
useLayoutEffect é o caminho a seguir !!!! Essa resposta é minha pergunta sobre como verificar se o usuário está logado. (O problema era que os componentes carregariam e, em seguida, verifique se o usuário estava logado.) Mas minha pergunta é: esta é uma prática padrão? Eu não estou vendo em muitos lugares
Jessica
1
Sim, é bastante comum; mencionado também nos documentos oficiais do React - apenas em texto menor por causa das ramificações da renderização dupla do DOM para executar a lógica antes que o usuário note.
rob2d
Na verdade, ele é executado após renderizar o componente. Portanto, é totalmente diferente do componentWillMount.
Jiri Mihal 26/03
7

Esta é a maneira como eu simulo o construtor em componentes funcionais usando o useRefgancho:

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>);
}

Aqui está o exemplo do ciclo de vida:

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

É claro que os componentes da classe não possuem Bodyetapas, não é possível fazer uma simulação 1: 1 devido a diferentes conceitos de funções e classes.

Jiri Mihal
fonte
Não mergulhei no seu exemplo, mas seu primeiro trecho de código funciona para mim, obrigado!
SAndriy 24/04
6

Eu escrevi um gancho personalizado que executará uma função uma vez antes da primeira renderização.

useBeforeFirstRender.js

import { useState, useEffect } from 'react'

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

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

  if (!hasRendered) {
    fun()
  }
}

Uso:

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


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

  return (
    <div>
      My component
    </div>
  )
}
Pouco tempo
fonte
3

Há uma boa solução alternativa para implementar componentDidMounte componentWillUnmountcom useEffect.

Com base na documentação, useEffectpode retornar uma função "limpeza". essa função não será chamada na primeira useEffectchamada, apenas nas chamadas subseqüentes.

Portanto, se usarmos o useEffectgancho sem nenhuma dependência, o gancho será chamado apenas quando o componente estiver montado e a função "limpeza" será chamada quando o componente estiver desmontado.

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

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

A chamada da função de retorno de limpeza é chamada apenas quando o componente é desmontado.

Espero que isto ajude.

AfikDeri
fonte
2
Como isso ajuda se não tem nada a ver com componentWillMount ? Estou esquecendo de algo?
ZenVentzi 13/11/19
Sim, você está faltando o fato de que na mesma useEffectchamada que você obter a mesma funcionalidade de componentWillMounte componentWillUnmountem uma agradável e limpo maneira
AfikDeri
Isso não é verdade, useEffectapenas é executado após uma renderização enquanto componentWillMounté executado antes da renderização do componente.
Overcode
@ Overcode eu estava falando componentDidMountnão componentWillMount. Eu perdi isso na pergunta, meu mal.
AfikDeri 27/01
1

Você pode hackear o gancho useMemo para imitar um evento do ciclo de vida componentWillMount. Apenas faça:

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

Você precisaria manter o gancho useMemo antes de qualquer coisa que interaja com seu estado. Não é assim que se destina, mas funcionou para mim em todos os problemas do componentWillMount.

Isso funciona porque o useMemo não precisa realmente retornar um valor e você não precisa usá-lo como nada, mas como ele memoriza um valor com base nas dependências que serão executadas apenas uma vez ("[]") e estão no topo de nosso componente, é executado uma vez quando o componente é montado antes de qualquer outra coisa.

jpmarks
fonte
0

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

Lembre-se de que a função passada para useMemo é executada durante a renderização. Não faça nada que normalmente não faria durante a renderização. Por exemplo, os efeitos colaterais pertencem a useEffect, não useMemo.


fonte
O usememo é para otimização de desempenho. Um gancho será renderizado novamente depois de já estar montado, se um suporte for alterado, o que anula o objetivo do autor.
max54 23/05/19
0

A resposta de Ben Carp parece ser apenas uma válida para mim.

Porém, como estamos usando maneiras funcionais, apenas outra abordagem pode se beneficiar do fechamento e do HoC:

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

Então use-o:

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

Resposta curta à sua pergunta original , como componentWillMountpode ser usada com os ganchos de reação:

componentWillMountfoi descontinuado e considerado legado . Reagir recomendação :

Geralmente, recomendamos o uso do construtor () para inicializar o estado.

Agora, nas Perguntas frequentes sobre o Hook, você descobre qual é o equivalente a um construtor de classe para componentes de função:

construtor: Os componentes da função não precisam de um construtor. Você pode inicializar o estado na chamada useState. Se a computação do estado inicial for cara, você pode passar uma função para useState.

Portanto, um exemplo de uso se componentWillMountparece com isso:

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; };
ford04
fonte
0

Simplesmente adicione um array de dependência vazio em useEffect, ele funcionará como componentDidMount.

useEffect(() => {
  // Your code here
  console.log("componentDidMount")
}, []);
Codificação
fonte
0

Há um truque simples para simular o componentDidMounte componentWillUnmountusando useEffect:

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

  return () => {
    console.log("componentWillUnmount");
  };
}, []);
AmerllicA
fonte