Resposta curta:
React garante que refs são colocados antes componentDidMountou componentDidUpdateganchos. Mas apenas para crianças que realmente foram renderizadas .
componentDidMount() {
}
componentDidUpdate() {
}
render() {
return <div ref={/* ... */} />;
}
Observe que isso não significa “React sempre define todas as referências antes que esses ganchos sejam executados”.
Vejamos alguns exemplos em que os refs não são definidos.
Refs não são definidos para elementos que não foram renderizados
O React só chamará retornos de chamada ref para os elementos que você realmente retornou da renderização .
Isso significa que se o seu código for semelhante
render() {
if (this.state.isLoading) {
return <h1>Loading</h1>;
}
return <div ref={this._setRef} />;
}
e inicialmente this.state.isLoadingé true, você não deve esperar this._setRefser chamado antes componentDidMount.
Isso deve fazer sentido: se sua primeira renderização retornou <h1>Loading</h1>, não há como o React saber que sob alguma outra condição ele retorna algo que precisa de um ref para ser anexado. Também não há nada para definir o ref: o <div>elemento não foi criado porque o render()método disse que ele não deveria ser renderizado.
Portanto, com este exemplo, apenas componentDidMountdisparará. No entanto, quando this.state.loadingmudar parafalse , você verá o this._setRefanexo primeiro e, em seguida, componentDidUpdateirá disparar.
Cuidado com outros componentes
Observe que se você passar filhos com refs para outros componentes, há uma chance de que eles estejam fazendo algo que impede a renderização (e causa o problema).
Por exemplo, este:
<MyPanel>
<div ref={this.setRef} />
</MyPanel>
não funcionaria se MyPanelnão fosse incluído props.childrenem sua saída:
function MyPanel(props) {
return <h1>Oops, no refs for you today!</h1>;
}
Novamente, não é um bug: não haveria nada para o React definir o ref porque o elemento DOM não foi criado .
Refs não são definidos antes dos ciclos de vida se forem passados para um aninhado ReactDOM.render()
Semelhante à seção anterior, se você passar um filho com um ref para outro componente, é possível que esse componente faça algo que evite anexar o ref a tempo.
Por exemplo, talvez ele não esteja retornando a criança de render()e, em vez disso, esteja chamando ReactDOM.render()um gancho de ciclo de vida. Você pode encontrar um exemplo disso aqui . Nesse exemplo, renderizamos:
<MyModal>
<div ref={this.setRef} />
</MyModal>
Mas MyModalrealiza uma ReactDOM.render()chamada em seu componentDidUpdate método de ciclo de vida:
componentDidUpdate() {
ReactDOM.render(this.props.children, this.targetEl);
}
render() {
return null;
}
Desde o React 16, essas chamadas de renderização de nível superior durante um ciclo de vida serão atrasadas até que os ciclos de vida sejam executados para toda a árvore . Isso explicaria por que você não está vendo os árbitros anexados a tempo.
A solução para esse problema é usar
portais em vez de ReactDOM.renderchamadas aninhadas :
render() {
return ReactDOM.createPortal(this.props.children, this.targetEl);
}
Desta forma, nosso <div>com um ref é realmente incluído na saída do render.
Portanto, se você encontrar esse problema, precisa verificar se não há nada entre seu componente e o ref que possa atrasar a renderização dos filhos.
Não use setStatepara armazenar refs
Certifique-se de que você não está usando setStatepara armazenar o ref no retorno de chamada ref, pois ele é assíncrono e antes de "terminar", componentDidMountserá executado primeiro.
Ainda é um problema?
Se nenhuma das dicas acima ajudar, registre um problema no React e nós daremos uma olhada.
thisdo escopo léxico fora de sua classe. Tente se livrar da sintaxe da função de seta para seus métodos de classe e veja se isso ajuda.