Problema
Estou definindo uma reação ref
usando uma definição de função inline
render = () => {
return (
<div className="drawer" ref={drawer => this.drawerRef = drawer}>
então, no componentDidMount
DOM, a referência não está definida
componentDidMount = () => {
// this.drawerRef is not defined
Meu entendimento é que o ref
retorno de chamada deve ser executado durante a montagem, no entanto, adicionar console.log
instruções revela componentDidMount
é chamado antes da função de retorno de chamada ref.
Outros exemplos de código que examinei, por exemplo, esta discussão no github indicam a mesma suposição, componentDidMount
devem ser chamados após qualquer ref
retorno de chamada definido em render
, é até mesmo declarado na conversa
Então, componentDidMount é disparado após todos os callbacks ref terem sido executados?
Sim.
Estou usando o react 15.4.1
Outra coisa que tentei
Para verificar se a ref
função estava sendo chamada, tentei defini-la na classe como tal
setDrawerRef = (drawer) => {
this.drawerRef = drawer;
}
então em render
<div className="drawer" ref={this.setDrawerRef}>
O registro do console, neste caso, revela que o retorno de chamada está realmente sendo chamado após componentDidMount
fonte
this
do 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.render
e, portanto, precisávamos aproveitarcomponentDidUpdate
, já quecomponentDidMount
não faz parte do ciclo de vida de atualização . Provavelmente não é seu problema, mas pensei que valeria a pena levantar como uma solução potencial.ref callbacks are invoked before componentDidMount or componentDidUpdate lifecycle hooks.
mas isso não parece ser verdade :(ref = {ref => { this.drawerRef = ref }}
2. até mesmo refs são invocados antes de componentDidMount; ref só pode ser acessado após a renderização inicial quando o div no seu caso é renderizado. Portanto, você deve ser capaz de acessar o ref no próximo nível, ou seja, em componentWillReceiveProps usandothis.drawerRef
3. Se você tentar acessar antes da montagem inicial, obterá apenas os valores indefinidos de ref.Respostas:
Resposta curta:
React garante que refs são colocados antes
componentDidMount
oucomponentDidUpdate
ganchos. Mas apenas para crianças que realmente foram renderizadas .componentDidMount() { // can use any refs here } componentDidUpdate() { // can use any refs here } render() { // as long as those refs were rendered! 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 esperarthis._setRef
ser chamado antescomponentDidMount
.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 orender()
método disse que ele não deveria ser renderizado.Portanto, com este exemplo, apenas
componentDidMount
disparará. No entanto, quandothis.state.loading
mudar parafalse
, você verá othis._setRef
anexo primeiro e, em seguida,componentDidUpdate
irá 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
MyPanel
não fosse incluídoprops.children
em sua saída:function MyPanel(props) { // ignore props.children 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 chamandoReactDOM.render()
um gancho de ciclo de vida. Você pode encontrar um exemplo disso aqui . Nesse exemplo, renderizamos:<MyModal> <div ref={this.setRef} /> </MyModal>
Mas
MyModal
realiza umaReactDOM.render()
chamada em seucomponentDidUpdate
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.render
chamadas 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
setState
para armazenar refsCertifique-se de que você não está usando
setState
para armazenar o ref no retorno de chamada ref, pois ele é assíncrono e antes de "terminar",componentDidMount
será executado primeiro.Ainda é um problema?
Se nenhuma das dicas acima ajudar, registre um problema no React e nós daremos uma olhada.
fonte
Uma observação diferente do problema.
Percebi que o problema só ocorreu durante o modo de desenvolvimento. Após mais investigação, descobri que desativar a
react-hot-loader
configuração do meu Webpack evita esse problema.estou usando
E é um aplicativo de elétrons.
Minha configuração de desenvolvimento parcial do Webpack
const webpack = require('webpack') const merge = require('webpack-merge') const baseConfig = require('./webpack.config.base') module.exports = merge(baseConfig, { entry: [ // REMOVED THIS -> 'react-hot-loader/patch', `webpack-hot-middleware/client?path=http://localhost:${port}/__webpack_hmr`, '@babel/polyfill', './app/index' ], ... })
Fiquei desconfiado quando vi que usar a função embutida em render () estava funcionando, mas usar um método vinculado estava travando.
Funciona em qualquer caso
class MyComponent { render () { return ( <input ref={(el) => {this.inputField = el}}/> ) } }
Bater com react-hot-loader (ref é indefinida em componentDidMount)
class MyComponent { constructor (props) { super(props) this.inputRef = this.inputRef.bind(this) } inputRef (input) { this.inputField = input } render () { return ( <input ref={this.inputRef}/> ) } }
Para ser honesto, o hot reload costuma ser problemático para "acertar". Com as ferramentas de desenvolvimento atualizadas rapidamente, cada projeto tem uma configuração diferente. Talvez minha configuração particular pudesse ser corrigida. Avisarei você aqui se for o caso.
fonte
O problema também pode surgir quando você tenta usar um ref de um componente não montado, como usar um ref em setinterval e não limpa o intervalo definido durante a desmontagem do componente.
componentDidMount(){ interval_holder = setInterval(() => { this.myref = "something";//accessing ref of a component }, 2000); }
intervalo sempre claro, como por exemplo,
componentWillUnmount(){ clearInterval(interval_holder) }
fonte