componentDidMount가 참조 콜백 전에 호출 됨


87

문제

ref인라인 함수 정의를 사용하여 반응 을 설정하고 있습니다.

render = () => {
    return (
        <div className="drawer" ref={drawer => this.drawerRef = drawer}>

그런 다음 componentDidMountDOM 참조가 설정되지 않았습니다.

componentDidMount = () => {
    // this.drawerRef is not defined

내 이해는 ref콜백이 마운트 중에 실행되어야한다는 것입니다. 그러나 console.log문을 추가 하면 ref 콜백 함수 componentDidMount가 호출 되기 전에 호출됩니다.

내가 예를 들어 검토 한 다른 코드 샘플 이 토론 GitHub의에서이 같은 가정을 나타낸다는 componentDidMount호출 할 필요가 이후 어떤 ref정의 콜백 render이도있어, 대화에 명시된

그래서 모든 ref 콜백이 실행 된 후 componentDidMount가 시작됩니까?

예.

반응 15.4.1을 사용하고 있습니다.

내가 시도한 다른 것

ref함수가 호출되고 있는지 확인하기 위해 클래스에서 다음과 같이 정의 해 보았습니다.

setDrawerRef = (drawer) => {
  this.drawerRef = drawer;
}

그런 다음 render

<div className="drawer" ref={this.setDrawerRef}>

이 경우 콘솔 로깅은 콜백이 실제로 호출되고 계시 componentDidMount


6
내가 틀렸을 수도 있지만 렌더링 메 토스에 화살표 함수를 사용하면 this클래스 외부의 어휘 범위에서 값을 캡처합니다 . 클래스 메서드에 대한 화살표 함수 구문을 제거하고 도움이되는지 확인하십시오.
Yoshi

3
@GProst 그것이 내 질문의 본질입니다. 두 함수에 console.log를 넣고 componentDidMount가 먼저 실행되고 ref 콜백이 두 번째로 실행됩니다.
quickshiftin

3
그냥 기본적으로, 우리가 그것을 놓친, 우리를 위해 유사한 issue-- 있었다 초기 render따라서 레버리지로 필요 componentDidUpdatecomponentDidMount갱신의 일부가 아닌 라이프 사이클 . 아마도 귀하의 문제는 아니지만 잠재적 인 해결책으로 제기 할 가치가 있다고 생각했습니다.
Alexander Nied

4
React 16과 동일합니다. 문서에 명확하게 명시되어 ref callbacks are invoked before componentDidMount or componentDidUpdate lifecycle hooks.있지만 사실이 아닌 것 같습니다 :(
Ryan H.

1
1. 참조 화살표 선언은 다음과 같습니다. ref = {ref => { this.drawerRef = ref }}2. componentDidMount 전에 참조도 호출됩니다. ref는 귀하의 경우 div가 렌더링 될 때 초기 렌더링 후에 만 ​​액세스 할 수 있습니다. 따라서 다음 레벨, 즉 componentWillReceiveProps에서 this.drawerRef3을 사용하여 ref에 액세스 할 수 있어야합니다. 초기 마운트 전에 액세스를 시도하면 정의되지 않은 ref 값 중 하나만 얻을 수 있습니다.
bh4r4th

답변:


156

짧은 대답:

React는 ref가 componentDidMount또는 componentDidUpdatehooks 이전에 설정되도록 보장합니다 . 그러나 실제로 렌더링 된 어린이에게만 해당 됩니다 .

componentDidMount() {
  // can use any refs here
}

componentDidUpdate() {
  // can use any refs here
}

render() {
  // as long as those refs were rendered!
  return <div ref={/* ... */} />;
}

이것이 "React는 이러한 후크가 실행되기 전에 항상 모든 참조를 설정 합니다 "를 의미하지는 않습니다 .
참조 가 설정 되지 않은 몇 가지 예를 살펴 보겠습니다 .


렌더링되지 않은 요소에 대한 참조가 설정되지 않습니다.

React는 render에서 실제로 반환 한 요소에 대해서만 ref 콜백을 호출 합니다.

이것은 코드가

render() {
  if (this.state.isLoading) {
    return <h1>Loading</h1>;
  }

  return <div ref={this._setRef} />;
}

그리고 처음 this.state.isLoading입니다 true당신이해야 하지 기대 this._setRef하기 전에 호출 할 componentDidMount.

이것은 의미가있을 것입니다 : 만약 당신의 첫 번째 렌더가 반환 되었다면 <h1>Loading</h1>React가 어떤 다른 조건에서 ref가 첨부되어야하는 다른 것을 반환한다는 것을 알 수있는 방법이 없습니다. 또한이 없다 :에 심판 설정하는 것도<div> 때문에 요소가 작성되지 않았습니다 render()방법은 렌더링되지 않을 것이라고 말했다는.

따라서이 예에서는 만 componentDidMount실행됩니다. 그러나 경우 this.state.loading에 변경false , 당신은 볼 것이다 this._setRef첫째 부착 한 다음 componentDidUpdate실행됩니다.


다른 구성 요소에주의

참고 것을 당신이 다른 구성 요소에 이르기까지 심판 어린이를 전달하는 경우 가 방지 렌더링 (그리고 문제로 인해) 뭔가를하고있는 기회가있다.

예를 들면 다음과 같습니다.

<MyPanel>
  <div ref={this.setRef} />
</MyPanel>

출력에 MyPanel포함하지 않으면 작동 하지 않습니다 props.children.

function MyPanel(props) {
  // ignore props.children
  return <h1>Oops, no refs for you today!</h1>;
}

다시 말하지만, 그것은 버그가 아닙니다 : DOM 요소가 생성되지 않았기 때문에 React가 ref를 설정할 아무것도 없을 것입니다 .


참조가 중첩으로 전달되는 경우 수명주기 전에 설정되지 않습니다. ReactDOM.render()

이전 섹션과 마찬가지로 ref가있는 자식을 다른 구성 요소에 전달하면이 구성 요소가 시간 내에 참조를 첨부하지 못하게하는 작업을 수행 할 수 있습니다.

예를 들어에서 자식을 반환하지 render()않고 대신 ReactDOM.render()수명주기 후크를 호출 할 수 있습니다. 여기 에서 이에 대한 예를 찾을 수 있습니다 . 이 예에서는 다음을 렌더링합니다.

<MyModal>
  <div ref={this.setRef} />
</MyModal>

그러나 MyModal수행 ReactDOM.render()에 호출 componentDidUpdate 수명주기 방법 :

componentDidUpdate() {
  ReactDOM.render(this.props.children, this.targetEl);
}

render() {
  return null;
}

React 16 이후 로 수명주기 동안 이러한 최상위 렌더링 호출은 전체 트리에 대해 수명주기가 실행될 때까지 지연됩니다 . 이것은 왜 당신이 시간에 첨부 된 심판을 보지 못하는지 설명 할 것입니다.

이 문제에 대한 해결책 은 중첩 된 호출 대신 포털 을 사용하는 것입니다 ReactDOM.render.

render() {
  return ReactDOM.createPortal(this.props.children, this.targetEl);
}

이렇게 <div>하면 참조가있는 우리 가 실제로 렌더링 출력에 포함됩니다.

따라서이 문제가 발생하면 구성 요소와 참조 사이에 자식 렌더링을 지연시킬 수있는 요소가 없는지 확인해야합니다.

참조 setState를 저장 하는 데 사용하지 마십시오.

setStateref 콜백에 ref를 저장하는 데 사용 하고 있지 않은지 확인하십시오. 이는 비동기적이고 "완료"되기 전에 componentDidMount먼저 실행될 것입니다.


여전히 문제가 있습니까?

위의 팁 중 어느 것도 도움이되지 않는 경우 React에 문제를 제출하면 살펴 보겠습니다.


2
나는이 상황을 설명하기 위해 내 대답을 편집했습니다. 첫 번째 섹션을 참조하십시오. 도움이 되었기를 바랍니다!
Dan Abramov

안녕하세요 @ DanAbramov 감사합니다! 불행히도 처음 만났을 때 재현 가능한 사례를 개발할 수 없었습니다. 안타깝게도 저는 더 이상 그 프로젝트에서 일하지 않으며 그 이후로 재현 할 수 없습니다. 하지만이 질문은 충분히 인기를 얻었습니다. 많은 사람들이 문제를 겪고있는 것처럼 보이기 때문에 재현 가능한 케이스를 찾는 것이 중요하다는 데 동의합니다.
quickshiftin

많은 경우 오해 때문인 것 같습니다. React 15에서 이것은 삼킨 오류로 인해 발생할 수도 있습니다 (React 16은 더 나은 오류 처리를 제공하고 이것을 방지합니다). 이런 일이 발생하면 더 많은 사례를 검토하게되어 기쁩니다. 댓글에 자유롭게 추가해주세요.
Dan Abramov

도움! 나는 프리 로더가 있다는 사실을 몰랐습니다.
Nazariy

1
이 답변은 정말 도움이되었습니다. 비어있는 "refs"참조로 고생하고 있었는데 "요소"가 전혀 렌더링되지 않는 것으로 나타났습니다.
MarkSkayff

1

문제에 대한 다른 관찰.

이 문제는 개발 모드에서만 발생한다는 것을 깨달았습니다. 더 많은 조사 결과, react-hot-loaderWebpack 구성에서 비활성화 하면이 문제가 방지 된다는 것을 알았습니다 .

나는 사용하고있다

  • "반응 핫 로더": "3.1.3"
  • "webpack": "4.10.2",

그리고 그것은 전자 앱입니다.

내 부분적인 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'
  ],
  ...
})

render ()에서 인라인 함수를 사용하는 것이 작동하는 것을 보았을 때 의심 스러웠지만 바운드 메서드를 사용하면 충돌이 발생했습니다.

어떤 경우에도 작동

class MyComponent {
  render () {
    return (
      <input ref={(el) => {this.inputField = el}}/>
    )
  }
}

react-hot-loader와 충돌 (ref는 componentDidMount에서 정의되지 않음)

class MyComponent {
  constructor (props) {
    super(props)
    this.inputRef = this.inputRef.bind(this)
  }

  inputRef (input) {
    this.inputField = input
  }

  render () {
    return (
      <input ref={this.inputRef}/>
    )
  }
}

솔직히 말해서, hot reload는 "올바르게"되기 위해 종종 문제가되었습니다. 개발 도구가 빠르게 업데이트되므로 모든 프로젝트는 다른 구성을 갖습니다. 내 특정 구성이 수정 될 수 있습니다. 이 경우 여기에서 알려 드리겠습니다.


이것은 CodePen에서 문제가 발생하는 이유를 설명 할 수 있지만 인라인 함수를 사용하는 것이 제 경우에는 도움이되지 않았습니다.
robartsd

0

setinterval에서 참조를 사용하는 것과 같이 마운트 해제 된 구성 요소의 참조를 사용하려고하고 구성 요소 마운트 해제 중에 설정된 간격을 지우지 않는 경우에도 문제가 발생할 수 있습니다.

componentDidMount(){
    interval_holder = setInterval(() => {
    this.myref = "something";//accessing ref of a component
    }, 2000);
  }

예를 들어 항상 명확한 간격,

componentWillUnmount(){
    clearInterval(interval_holder)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.