Reactフォームでの小道具の変更状態の更新


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は非推奨になりました:代わりにgetDerivedStateFromPropsを使用してください

私が正しく理解している場合、それを独自の状態に割り当てるコンポーネントに渡さstart_timeれる親コンポーネントがありModalBodyますか?そして、子コンポーネントではなく親からその時間を更新したいとします。

Reactには、このシナリオに対処するためのヒントがいくつかあります。(注意:これはWebから削除された古い記事です。コンポーネントのプロパティに関する現在のドキュメントへのリンクです)。

小道具を使用して状態を生成getInitialStateすると、「真実のソース」、つまり実際のデータの場所が重複することがよくあります。これはgetInitialState、コンポーネントが最初に作成されたときにのみ呼び出されるためです。

可能な限り、オンザフライで値を計算して、後で同期が外れてメンテナンスの問題が発生しないようにします。

基本的に、親propsを子に割り当てるたびにstate、レンダーメソッドがプロップの更新時に呼び出されるとは限りません。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
16を反応させるの非推奨

7
@dudeまだ非推奨ではありません。あなたが参照しているのは、将来の参照のための目印です。引用します[..]going to be deprecated in the future
paddotk 2018

7
@poepjeまだ非推奨ではないかもしれませんが、現在の標準では安全ではないと見なされており、おそらく回避する必要があります
unflores

12
では、componentWillReceivePropsが廃止された後にこれを行うための新しい方法は何でしょうか?
ボリスD.テハロフ

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
FYI、あなたも返す必要がnull何も右のあなたの場合は後に、あなたが行く必要がありますので、変更するべきではない場合return null
Ilgıtユルドゥルム

@IlgıtYıldırım-4人があなたのコメントに賛成してからコードを編集しました-本当に違いがありますか?
ErichBSchulz 2018

そこ異なるオプションの深さに行くかなり良いリソースがあると、あなたがいずれかを使用する理由getDerivedStateFromPropsやメモ化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}
/>

そのパフォーマンスについて:

これは遅いように聞こえるかもしれませんが、パフォーマンスの違いは通常わずかです。コンポーネントに更新時に実行される重いロジックがある場合、そのサブツリーの差分がバイパスされるため、キーを使用するとさらに高速になります。


1
鍵、秘密!上記のようにReact 16で完全に動作します
ダレンスウィーニー

オブジェクトであり、一意の文字列がない場合、キーは機能しません
user3468806

キーはオブジェクトに対して機能しますが、私はそれを行いました。もちろん、キーには一意の文字列がありました。
tsujp

@ user3468806外部参照を持つ複雑なオブジェクトでない場合は、を使用JSON.stringify(myObject)して、オブジェクトから一意のキーを派生できます。
Roy Prins

24

利用可能なcomponentDidUpdateもあります。

関数signatur:

componentDidUpdate(prevProps, prevState, snapshot)

これは、コンポーネントが更新されたときにDOMを操作する機会として使用します。初期状態では呼び出されませんrender

参照してください、おそらく派生パターンは必要ありません。記事には、との両方のアンチパターンが記載されcomponentDidUpdateていgetDerivedStateFromPropsます。とても重宝しました。


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. 完全コンポーネント制御:ParentComponentModalBody所有になるstart_time状態を。この場合、モーダルがこの状態を所有する必要があると思うので、これは私の好みのアプローチではありません。
  2. キーを持つ完全に制御されていないコンポーネント:これは私の好みのアプローチです。反応ドキュメントの例:https : //codesandbox.io/s/6v1znlxyxn。あなたはあなたstart_timeから状態を完全に所有し、あなたがすでにしたようにModalBody使用しますgetInitialStatestart_time状態をリセットするには、キーをParentComponent


0

メモ化を使用する

opの状態の導出は、小道具の直接操作であり、真の導出は必要ありません。つまり、直接利用または変換できる小道具がある場合、小道具をオン状態で保存する必要はありません

の状態値start_timeが単にprop である とすると、prop start_time.format("HH:mm")に含まれる情報はそれ自体でコンポーネントの更新に十分です。

ただし、プロップの変更時にのみフォーマットを呼び出したい場合、最新のドキュメントごとにこれを行う正しい方法は、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.