[javascript] React-마운트 해제 된 구성 요소의 setState ()

내 반응 구성 요소에서 ajax 요청이 진행되는 동안 간단한 회 전자를 구현하려고합니다-im은 상태를 사용하여 로딩 상태를 저장합니다.

어떤 이유로 내 React 구성 요소의 아래 코드 부분 에서이 오류가 발생합니다.

마운트되거나 마운트 된 구성 요소 만 업데이트 할 수 있습니다. 이것은 일반적으로 마운트되지 않은 구성 요소에서 setState ()를 호출했음을 의미합니다. 이건 안돼. 정의되지 않은 구성 요소에 대한 코드를 확인하십시오.

첫 번째 setState 호출을 제거하면 오류가 사라집니다.

constructor(props) {
  super(props);
  this.loadSearches = this.loadSearches.bind(this);

  this.state = {
    loading: false
  }
}

loadSearches() {

  this.setState({
    loading: true,
    searches: []
  });

  console.log('Loading Searches..');

  $.ajax({
    url: this.props.source + '?projectId=' + this.props.projectId,
    dataType: 'json',
    crossDomain: true,
    success: function(data) {
      this.setState({
        loading: false
      });
    }.bind(this),
    error: function(xhr, status, err) {
      console.error(this.props.url, status, err.toString());
      this.setState({
        loading: false
      });
    }.bind(this)
  });
}

componentDidMount() {
  setInterval(this.loadSearches, this.props.pollInterval);
}

render() {

    let searches = this.state.searches || [];


    return (<div>
          <Table striped bordered condensed hover>
          <thead>
            <tr>
              <th>Name</th>
              <th>Submit Date</th>
              <th>Dataset &amp; Datatype</th>
              <th>Results</th>
              <th>Last Downloaded</th>
            </tr>
          </thead>
          {
          searches.map(function(search) {

                let createdDate = moment(search.createdDate, 'X').format("YYYY-MM-DD");
                let downloadedDate = moment(search.downloadedDate, 'X').format("YYYY-MM-DD");
                let records = 0;
                let status = search.status ? search.status.toLowerCase() : ''

                return (
                <tbody key={search.id}>
                  <tr>
                    <td>{search.name}</td>
                    <td>{createdDate}</td>
                    <td>{search.dataset}</td>
                    <td>{records}</td>
                    <td>{downloadedDate}</td>
                  </tr>
                </tbody>
              );
          }
          </Table >
          </div>
      );
  }

질문은 구성 요소가 이미 마운트되어야 할 때 (componentDidMount에서 호출 됨) 왜이 오류가 발생합니까? 구성 요소가 마운트되면 상태를 설정하는 것이 안전하다고 생각합니까?



답변

렌더링 기능을 보지 않고는 조금 힘들다. 해야 할 일을 이미 발견 할 수 있지만 인터벌을 사용할 때마다 언 마운트시이를 지워야합니다. 그래서:

componentDidMount() {
    this.loadInterval = setInterval(this.loadSearches, this.props.pollInterval);
}

componentWillUnmount () {
    this.loadInterval && clearInterval(this.loadInterval);
    this.loadInterval = false;
}

이러한 성공 및 오류 콜백은 마운트 해제 후에도 계속 호출 될 수 있으므로 간격 변수를 사용하여 마운트되었는지 확인할 수 있습니다.

this.loadInterval && this.setState({
    loading: false
});

이것이 도움이되기를 바랍니다. 이것이 작업을 수행하지 않으면 렌더링 기능을 제공하십시오.

건배


답변

질문은 구성 요소가 이미 마운트되어야 할 때 (componentDidMount에서 호출 됨) 왜이 오류가 발생합니까? 구성 요소가 마운트되면 상태를 설정하는 것이 안전하다고 생각합니까?

그것은되어 있지 에서 호출 componentDidMount. 귀하의 componentDidMount급부상하지 않는 스택, 타이머 핸들러의 스택에서 실행됩니다 콜백 함수 componentDidMount. 분명히 콜백 ( this.loadSearches)이 실행될 때 구성 요소가 마운트 해제되었습니다.

그래서 받아 들여진 대답은 당신을 보호 할 것입니다. 비동기 함수 (일부 핸들러에 이미 제출 됨)를 취소 할 수없는 다른 비동기 API를 사용하는 경우 다음을 수행 할 수 있습니다.

if (this.isMounted())
     this.setState(...

이는 특히 API가 취소 기능을 제공하는 경우 (에서 setInterval와 같이 clearInterval) 러그 아래에서 물건을 훑어 보는 것처럼 느껴지지만 모든 경우에보고하는 오류 메시지를 제거합니다 .


답변

다른 옵션이 필요한 사람에게는 ref 속성의 콜백 메서드가 해결 방법이 될 수 있습니다. handleRef의 매개 변수는 div DOM 요소에 대한 참조입니다.

refs 및 DOM에 대한 자세한 정보 : https://facebook.github.io/react/docs/refs-and-the-dom.html

handleRef = (divElement) => {
 if(divElement){
  //set state here
 }
}

render(){
 return (
  <div ref={this.handleRef}>
  </div>
 )
}


답변

class myClass extends Component {
  _isMounted = false;

  constructor(props) {
    super(props);

    this.state = {
      data: [],
    };
  }

  componentDidMount() {
    this._isMounted = true;
    this._getData();
  }

  componentWillUnmount() {
    this._isMounted = false;
  }

  _getData() {
    axios.get('https://example.com')
      .then(data => {
        if (this._isMounted) {
          this.setState({ data })
        }
      });
  }


  render() {
    ...
  }
}


답변

후세를 위해

우리의 경우이 오류는 Reflux, 콜백, 리디렉션 및 setState와 관련이 있습니다. onDone 콜백에 setState를 보냈지 만 onSuccess 콜백에 대한 리디렉션도 보냈습니다. 성공한 경우 onSuccess 콜백은 onDone 전에 실행 됩니다. 이로 인해 시도 된 setState 이전에 리디렉션발생합니다 . 따라서 마운트되지 않은 구성 요소에 대한 오류 setState.

환류 저장 조치 :

generateWorkflow: function(
    workflowTemplate,
    trackingNumber,
    done,
    onSuccess,
    onFail)
{...

수정 전 전화 :

Actions.generateWorkflow(
    values.workflowTemplate,
    values.number,
    this.setLoading.bind(this, false),
    this.successRedirect
);

수정 후 전화 :

Actions.generateWorkflow(
    values.workflowTemplate,
    values.number,
    null,
    this.successRedirect,
    this.setLoading.bind(this, false)
);

어떤 경우에는 React의 isMounted가 “deprecated / anti-pattern”이기 때문에 _mounted 변수를 사용하여 직접 모니터링했습니다.


답변

반응 후크로 활성화 된 솔루션을 공유합니다 .

React.useEffect(() => {
  let isSubscribed = true

  callApi(...)
    .catch(err => isSubscribed ? this.setState(...) : Promise.reject({ isSubscribed, ...err }))
    .then(res => isSubscribed ? this.setState(...) : Promise.reject({ isSubscribed }))
    .catch(({ isSubscribed, ...err }) => console.error('request cancelled:', !isSubscribed))

  return () => (isSubscribed = false)
}, [])

fetch id 변경에 대한 이전 요청 을 취소 할 때마다 동일한 솔루션을 확장 할 수 있습니다 this.setState.

React.useEffect(() => {
  let isCancelled = false

  callApi(id).then(...).catch(...) // similar to above

  return () => (isCancelled = true)
}, [id])

이것은 자바 스크립트의 클로저 덕분에 작동합니다 .

일반적으로 위의 아이디어 는 react doc에서 권장 하는 makeCancelable 접근 방식 에 가깝습니다.

isMounted는 Antipattern입니다.

신용

https://juliangaramendy.dev/use-promise-subscription/


답변