반응 양식의 소품 변경 상태 업데이트


184

React 양식에 문제가 있고 상태를 올바르게 관리하고 있습니다. 양식에 모달로 된 시간 입력 필드가 있습니다. 초기 값은의 상태 변수로 설정되며 getInitialState상위 구성 요소에서 전달됩니다. 이것은 그 자체로 잘 작동합니다.

부모 구성 요소를 통해 기본 start_time 값을 업데이트하려고 할 때 문제가 발생합니다. 업데이트 자체는를 통해 상위 구성 요소에서 발생합니다 setState start_time: new_time. 그러나 내 양식에서 기본 start_time 값은에 한 번만 정의되므로 변경되지 않습니다 getInitialState.

componentWillUpdate통해 상태를 강제로 변경 하려고 시도했지만 setState start_time: next_props.start_time실제로 작동했지만 Uncaught RangeError: Maximum call stack size exceeded오류가 발생했습니다.

내 질문은,이 경우 상태를 업데이트하는 올바른 방법은 무엇입니까? 어떻게 든이 잘못에 대해 생각하고 있습니까?

현재 코드 :

@ModalBody = React.createClass
  getInitialState: ->
    start_time: @props.start_time.format("HH:mm")

  #works but takes long and causes:
  #"Uncaught RangeError: Maximum call stack size exceeded"
  componentWillUpdate: (next_props, next_state) ->
    @setState(start_time: next_props.start_time.format("HH:mm"))

  fieldChanged: (fieldName, event) ->
    stateUpdate = {}
    stateUpdate[fieldName] = event.target.value
    @setState(stateUpdate)

  render: ->
    React.DOM.div
      className: "modal-body"
      React.DOM.form null,
        React.createElement FormLabelInputField,
          type: "time"
          id: "start_time"
          label_name: "Start Time"
          value: @state.start_time
          onChange: @fieldChanged.bind(null, "start_time”)

@FormLabelInputField = React.createClass
  render: ->
    React.DOM.div
      className: "form-group"
      React.DOM.label
        htmlFor: @props.id
        @props.label_name + ": "
      React.DOM.input
        className: "form-control"
        type: @props.type
        id: @props.id
        value: @props.value
        onChange: @props.onChange

답변:


287

반응식 16 이후 componentWillReceiveProps는 depcricated입니다 . 대신 getDerivedStateFromProps 를 사용하십시오 .

올바르게 이해하면 상위 구성 요소로 전달되는 상위 구성 요소가 있습니다 start_time.ModalBody 자신의 상태에 할당 구성 요소로 있습니까? 그리고 하위 구성 요소가 아닌 상위에서 해당 시간을 업데이트하려고합니다.

React에는이 시나리오를 다루는 데 유용한 정보가 있습니다. (이 기사는 웹에서 삭제 된 이전 기사입니다. 컴포넌트 props에 대한 현재 문서에 대한 링크가 있습니다.)

소품을 사용하여 상태를 생성하는 것은 getInitialState종종 "진실의 근원", 즉 실제 데이터가있는 곳의 복제로 이어집니다. 이 때문입니다getInitialState구성 요소를 처음 작성할 때만 호출 입니다.

가능할 때마다 값을 즉시 계산하여 나중에 동기화되지 않고 유지 보수 문제를 유발하지 않도록하십시오.

기본적으로 부모 props를 자식 에게 할당 할 때마다 stateprop 업데이트시 render 메서드가 항상 호출되는 것은 아닙니다. componentWillReceiveProps메소드를 사용하여 수동으로 호출해야합니다 .

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}

84
React 16에서 사용 중단됨
친구

7
@dude 아직 더 이상 사용되지 않습니다. 참조하는 것은 나중에 참조 할 수있는 머리입니다. 나는 인용한다[..]going to be deprecated in the future
paddotk

7
그것은 아직되지되지 않을 수 있지만,이 현재 표준에 의해 안전하지 않은 것으로 볼 때 아마도 피해야한다 @poepje
unflores

12
따라서 componentWillReceiveProps가 더 이상 사용되지 않는 새로운 방법은 무엇입니까?
Boris D. Teoharov

5
@Boris 이제 반응 팀은 기본적으로 당신에게 채워지라고 말합니다. getDerivedStateFromProps라는 새로운 메소드를 제공합니다. 캐치는 이것이 정적 방법이라는 것입니다. 즉, 새 상태를 즉시 반환해야하므로 상태를 업데이트하기 위해 비동기 적으로 작업을 수행 할 수 없으며 클래스 메서드 나 필드에 액세스 할 수 없습니다. 메모를 사용할 수도 있지만 모든 사용 사례에 맞는 것은 아닙니다. 다시 한 번 반응 팀은 작업 방식을 강요하려고합니다. 매우 어리 석고 무능력한 디자인 결정입니다.
ig-dev

76

분명히 상황이 바뀌고 있습니다 ... getDerivedStateFromProps () 가 이제 선호되는 기능입니다.

class Component extends React.Component {
  static getDerivedStateFromProps(props, current_state) {
    if (current_state.value !== props.value) {
      return {
        value: props.value,
        computed_prop: heavy_computation(props.value)
      }
    }
    return null
  }
}

(danburzo @ github 코드 위)


7
참고로, null만약 당신의 if 바로 뒤에 아무것도 바뀌지 않으면 돌아 가야합니다.return null
Ilgıt Yıldırım

@ IlgıtYıldırım-4 명이 댓글을 올린 이후 코드를 편집했습니다. 실제로 차이가 있습니까?
ErichBSchulz

다른 옵션들과 왜 당신이 getDerivedStateFromProps또는 memoization reactjs.org/blog/2018/06/07/…를
unflores

2
getDerivedStateFromProps는 강제로 정적입니다. 상태를 업데이트하기 위해 비동기식으로 작업을 수행 할 수 없으며 클래스 메서드 나 필드에 액세스 할 수 없습니다. 다시 한 번 반응 팀은 작업 방식을 강요하려고합니다. 매우 어리 석고 무능력한 디자인 결정입니다.
ig-dev

39

componentWillReceiveProps "종종 버그와 불일치가 발생하기 때문에"더 이상 사용되지 않습니다.

외부에서 무언가가 바뀌면으로 하위 구성 요소를 완전히 재설정하는 것이key 좋습니다.

key자식 구성 요소에 소품을 제공 key하면 외부 의 값이 변경 될 때마다이 구성 요소가 다시 렌더링됩니다. 예 :

<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

성능상 :

느리게 들릴 수 있지만 일반적으로 성능 차이는 중요하지 않습니다. 하위 트리에 대해 diffing이 무시되므로 구성 요소에 업데이트시 실행되는 논리가 많은 경우 키를 사용하는 것이 더 빠를 수도 있습니다.


1
열쇠, 비밀! 위에서 언급 한 바와 같이 완벽하게 16 반응 작품
대런 스위니

키가없는 작품이 대상이고 경우 할 일이 고유 한 문자열이 없습니다
user3468806

열쇠는 물체에 효과가 있습니다. 물론 키에 대한 고유 한 문자열이있었습니다.
tsujp

@ user3468806 외부 참조가있는 복잡한 객체가 아닌 경우 객체 JSON.stringify(myObject)에서 고유 키를 파생시키는 데 사용할 수 있습니다 .
Roy Prins

24

도 있습니다 componentDidUpdate 이 없습니다.

기능 signatur :

componentDidUpdate(prevProps, prevState, snapshot)

컴포넌트가 업데이트 될 때 DOM에서 작동 할 수있는 기회로 이것을 사용하십시오. initial에 호출되지 않습니다 render.

보기 당신은 아마 국가 파생하지 않아도 모두 안티 패턴을 설명 제, componentDidUpdategetDerivedStateFromProps. 나는 그것이 매우 유용하다는 것을 알았습니다.


나는 componentDidUpdate단순하고 대부분의 경우에 더 적합하기 때문에 사용 하게됩니다.
KeitelDOG

14

이 작업을 수행하는 새로운 후크 방법은 componentWillReceiveProps 대신 useEffect를 사용하는 것입니다.

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}

기능 후크 구동 구성 요소에서 다음이됩니다.

// store the startTime prop in local state
const [startTime, setStartTime] = useState(props.startTime)
// 
useEffect(() => {
  if (props.startTime !== startTime) {
    setStartTime(props.startTime);
  }
}, [props.startTime]);

setState를 사용하여 상태를 설정하고 useEffect를 사용하여 지정된 소품에 대한 변경 사항을 확인하고 소품 변경시 상태를 업데이트하는 조치를 취합니다.


5

당신은 아마 파생 국가가 필요하지 않습니다

1. 부모로부터 키를 설정

키가 변경되면 React는 현재 인스턴스를 업데이트하지 않고 새 컴포넌트 인스턴스를 만듭니다. 키는 일반적으로 동적 목록에 사용되지만 여기서도 유용합니다.

2. getDerivedStateFromProps/ 사용componentWillReceiveProps

어떤 이유로 키가 작동하지 않는 경우 (아마도 구성 요소가 초기화하는 데 너무 비쌉니다)

를 사용 getDerivedStateFromProps하면 상태의 일부를 재설정 할 수 있지만 현재 약간 버그가 있습니다 (v16.7) ! , 사용법에 대해서는 위의 링크를 참조하십시오


2

반응 문서에서 : https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

소품 변경시 소거 상태가 안티 패턴

React 16부터 componentWillReceiveProps는 더 이상 사용되지 않습니다. 반응 문서 에서이 경우 권장되는 접근 방식은 사용입니다.

  1. 완전 구성 요소 제어 다음 ParentComponent의가 ModalBody자신의 것입니다 start_time상태. 이것은 모달 이이 상태를 소유해야한다고 생각하기 때문에이 경우에는 선호하는 접근법이 아닙니다.
  2. 키가있는 완전히 제어되지 않은 구성 요소 : 이것은 내가 선호하는 접근법입니다. :의 예는 문서 반응 https://codesandbox.io/s/6v1znlxyxn를 . 당신은 당신의 start_time상태를 완전히 소유하고 이미했던 것처럼 ModalBody사용 getInitialState합니다. start_time상태 를 재설정하려면 간단히ParentComponent


0

메모 사용

op의 국가 파생은 진정한 파생이 필요없는 소품을 직접 조작하는 것입니다. 즉, 직접 활용하거나 변형 할 수있는 소품이있는 경우 소품을 state에 저장할 필요없습니다 .

의 상태 값 start_time이 단순히 prop start_time.format("HH:mm")인 경우 prop 에 포함 된 정보는 이미 구성 요소를 업데이트하기에 충분합니다.

그러나 소품 변경시 형식을 호출하고 싶다면 최신 문서 마다이 작업을 수행하는 올바른 방법은 Memoize를 사용하는 것입니다 : https://reactjs.org/blog/2018/06/07/you-probably-dont- need-derived-state.html # what-about-memoization


-1

ref 사용이 안전하다고 생각합니다. 위의 방법에 신경 쓰지 마십시오.

class Company extends XComponent {
    constructor(props) {
        super(props);
        this.data = {};
    }
    fetchData(data) {
        this.resetState(data);
    }
    render() {
        return (
            <Input ref={c => this.data['name'] = c} type="text" className="form-control" />
        );
    }
}
class XComponent extends Component {
    resetState(obj) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property) && typeof this.data[property] !== 'undefined') {
                if ( obj[property] !== this.data[property].state.value )
                    this.data[property].setState({value: obj[property]});
                else continue;
            }
            continue;
        }
    }
}

이 응답은 암호 (코드를 읽을 수 없으며 OP 문제에 대한 설명 / 링크가 거의 없음)이며 OP의 문제를 다루지 않습니다. 이는 초기 상태를 처리하는 방법입니다.
netchkin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.