反応入力のdefaultValueが状態で更新されない


94

reactを使用して単純なフォームを作成しようとしていますが、データをフォームのdefaultValueに適切にバインドすることが困難になっています。

私が探している行動はこれです:

  1. ページを開くと、テキスト入力フィールドにデータベースのAwayMessageのテキストが入力されているはずです。それが「サンプルテキスト」です
  2. データベースのAwayMessageにテキストがない場合は、テキスト入力フィールドにプレースホルダーを配置するのが理想的です。

ただし、現在、ページを更新するたびに[テキスト]入力フィールドが空白になっています。(入力に入力した内容は適切に保存され、保持されます。)これは、AwayMessageが空のオブジェクトである場合に入力テキストフィールドのhtmlが読み込まれますが、awayMessageが読み込まれたときに更新されないためです。また、フィールドのデフォルト値を指定できません。

わかりやすくするために一部のコードを削除しました(つまり、onToggleChange)

    window.Pages ||= {}

    Pages.AwayMessages = React.createClass

      getInitialState: ->
        App.API.fetchAwayMessage (data) =>
        @setState awayMessage:data.away_message
        {awayMessage: {}}

      onTextChange: (event) ->
        console.log "VALUE", event.target.value

      onSubmit: (e) ->
        window.a = @
        e.preventDefault()
        awayMessage = {}
        awayMessage["master_toggle"]=@refs["master_toggle"].getDOMNode().checked
        console.log "value of text", @refs["text"].getDOMNode().value
        awayMessage["text"]=@refs["text"].getDOMNode().value
        @awayMessage(awayMessage)

      awayMessage: (awayMessage)->
        console.log "I'm saving", awayMessage
        App.API.saveAwayMessage awayMessage, (data) =>
          if data.status == 'ok'
            App.modal.closeModal()
            notificationActions.notify("Away Message saved.")
            @setState awayMessage:awayMessage

      render: ->
        console.log "AWAY_MESSAGE", this.state.awayMessage
        awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else "Placeholder Text"
        `<div className="away-messages">
           <div className="header">
             <h4>Away Messages</h4>
           </div>
           <div className="content">
             <div className="input-group">
               <label for="master_toggle">On?</label>
               <input ref="master_toggle" type="checkbox" onChange={this.onToggleChange} defaultChecked={this.state.awayMessage.master_toggle} />
             </div>
             <div className="input-group">
               <label for="text">Text</label>
               <input ref="text" onChange={this.onTextChange} defaultValue={awayMessageText} />
             </div>
           </div>
           <div className="footer">
             <button className="button2" onClick={this.close}>Close</button>
             <button className="button1" onClick={this.onSubmit}>Save</button>
           </div>
         </div>

AwayMessageのconsole.logには次のように表示されます。

AWAY_MESSAGE Object {}
AWAY_MESSAGE Object {id: 1, company_id: 1, text: "Sample Text", master_toggle: false}

回答:


61

defaultValueは初期ロード専用です

入力を初期化する場合はdefaultValueを使用する必要がありますが、状態を使用して値を変更する場合はvalueを使用する必要があります。個人的には、初期化している場合はdefaultValueを使用し、送信時に値を取得するにはrefsを使用するだけです。反応ドキュメントの参照と入力に関する詳細情報、https://facebook.github.io/react/docs/forms.htmlおよびhttps://facebook.github.io/react/docs/working-with-the- browser.html

入力を書き換える方法は次のとおりです。

awayMessageText = if this.state.awayMessage then this.state.awayMessage.text else ''
<input ref="text" onChange={this.onTextChange} placeholder="Placeholder Text" value={@state.awayMessageText} />

また、実際に値を「プレースホルダーテキスト」に設定するため、プレースホルダーテキストを渡したくありません。undefinedとnilは本質的に値をdefaultValueに変換するため、入力に空白の値を渡す必要があります。https://facebook.github.io/react/tips/control-input-null-value.html

getInitialStateはAPI呼び出しを行うことができません

getInitialStateの実行後にAPIを呼び出す必要があります。あなたの場合は、componentDidMountで行います。この例のhttps://facebook.github.io/react/tips/initial-ajax.htmlに従ってください

また、reactを使用してコンポーネントのライフサイクルを読むことをお勧めします。https://facebook.github.io/react/docs/component-specs.html

変更と読み込み状態で書き換え

個人的には、レンダリングのロジックが他にある場合は全体を実行したくないので、状態で「読み込み」を使用し、フォームが読み込まれる前にフォントの素晴らしいスピナーをレンダリングすることを好みます。http://fortawesome.github.io/Font- Awesome / examples /。ここに私が何を意味するかを示すための書き直しがあります。私がcjsxのティックをめちゃくちゃにした場合、それは私が通常このようにcoffeescriptを使用するだけだからです。

window.Pages ||= {}

Pages.AwayMessages = React.createClass

  getInitialState: ->
    { loading: true, awayMessage: {} }

  componentDidMount: ->
    App.API.fetchAwayMessage (data) =>
      @setState awayMessage:data.away_message, loading: false

  onToggleCheckbox: (event)->
    @state.awayMessage.master_toggle = event.target.checked
    @setState(awayMessage: @state.awayMessage)

  onTextChange: (event) ->
    @state.awayMessage.text = event.target.value
    @setState(awayMessage: @state.awayMessage)

  onSubmit: (e) ->
    # Not sure what this is for. I'd be careful using globals like this
    window.a = @
    @submitAwayMessage(@state.awayMessage)

  submitAwayMessage: (awayMessage)->
    console.log "I'm saving", awayMessage
    App.API.saveAwayMessage awayMessage, (data) =>
      if data.status == 'ok'
        App.modal.closeModal()
        notificationActions.notify("Away Message saved.")
        @setState awayMessage:awayMessage

  render: ->
    if this.state.loading
      `<i className="fa fa-spinner fa-spin"></i>`
    else
    `<div className="away-messages">
       <div className="header">
         <h4>Away Messages</h4>
       </div>
       <div className="content">
         <div className="input-group">
           <label for="master_toggle">On?</label>
           <input type="checkbox" onChange={this.onToggleCheckbox} checked={this.state.awayMessage.master_toggle} />
         </div>
         <div className="input-group">
           <label for="text">Text</label>
           <input ref="text" onChange={this.onTextChange} value={this.state.awayMessage.text} />
         </div>
       </div>
       <div className="footer">
         <button className="button2" onClick={this.close}>Close</button>
         <button className="button1" onClick={this.onSubmit}>Save</button>
       </div>
     </div>

それでカバーできます。これが、状態と値を使用するフォームを処理する1つの方法です。値の代わりにdefaultValueを使用し、refsを使用して送信時に値を取得することもできます。そのルートをたどるなら、データをフェッチしてそれをpropsとしてフォームに渡すための外部シェルコンポーネント(通常は高次コンポーネントと呼ばれます)を用意することをお勧めします。

全体的に、react docsを最後まで読み、いくつかのチュートリアルを行うことをお勧めします。世の中にはたくさんのブログがあり、http://www.egghead.ioには優れたチュートリアルがいくつかありました。私のサイトにもいくつかのものがあり、http://www.openmindedinnovations.com


初期状態を取得するときにAPI呼び出しを実行しても効果がないのはなぜですか?getInitialStateは、componentDidMountの直前に実行され、API呼び出しは非同期です。それはより一般的ですか、それとも背後に別の理由がありますか?
マイケル・マカロフ

1
私はそれをどこで読んだのか正確にはわかりませんが、あなたはそこにapi呼び出しを入れていません それを扱うために作られたライブラリgithub.com/andreypopp/react-asyncがあります。ただし、そのライブラリは使用せず、componentDidMountに配置します。反応チュートリアルのチュートリアルでは、componentDidMountでもAPI呼び出しを行うことを知っています。
Blaine Hatab、2015年

@MïchaelMakaröv--API呼び出しは非同期であり、getInitialStateは状態を同期的に返すためです。したがって、API呼び出しが完了する前に初期状態がセットアップされます。
ドロゴン2016年

2
defaultValueをvalueに置き換えても安全ですか?defaultValueは初期化時にのみロードされることを知っていますが、valueもこれを行うようです。
Stealthysnacks

2
@stealthysnacks値を使用することは問題ありませんが、今度は入力が機能するようにその値を設定する必要があります。defaultValueは初期値を設定するだけで、入力は変更できますが、値を使用すると「制御」されます
Blaine Hatab

59

これを修正する別の方法はkey、入力のを変更することです。

<input ref="text" key={this.state.awayMessage ? 'notLoadedYet' : 'loaded'} onChange={this.onTextChange} defaultValue={awayMessageText} />

更新:これは賛成票を獲得するので、コンテンツの読み込み中は適切にa disabledまたはreadonlyprop が必要であるため、UXエクスペリエンスを低下させないようにする必要があります。

そして、はい、それはおそらくハックですが、それは仕事を成し遂げます.. ;-)


素朴な実装-キー値が変更されている間に入力フィールドのフォーカスが変更される(たとえば、onKeyUpが消えた)
Arthur Kushman

2
ええ、それはいくつかの欠点がありましたが、仕事を簡単にやり遂げました。
TryingToImprove 2017年

これは賢いです。私は実際にそう selectであるkeyようにそれを使った。defaultValuevalue
Avi 2018

key入力の変更は、入力に反映される新しい値を取得するための鍵です。type="text"うまく使いました。
Jacob Nelson、

3

多分最善の解決策ではないかもしれませんが、コード内のあらゆる場所で再利用できるように、以下のようなコンポーネントを作成します。私はそれがデフォルトですでに反応していることを望みます。

<MagicInput type="text" binding={[this, 'awayMessage.text']} />

コンポーネントは次のようになります。

window.MagicInput = React.createClass

  onChange: (e) ->
    state = @props.binding[0].state
    changeByArray state, @path(), e.target.value
    @props.binding[0].setState state

  path: ->
    @props.binding[1].split('.')
  getValue: ->
    value = @props.binding[0].state
    path = @path()
    i = 0
    while i < path.length
      value = value[path[i]]
      i++
    value

  render: ->
    type = if @props.type then @props.type else 'input'
    parent_state = @props.binding[0]
    `<input
      type={type}
      onChange={this.onChange}
      value={this.getValue()}
    />`

ここで、配列による変更は、配列で表現されたパスによってハッシュにアクセスする関数です

changeByArray = (hash, array, newValue, idx) ->
  idx = if _.isUndefined(idx) then 0 else idx
  if idx == array.length - 1
    hash[array[idx]] = newValue
  else
    changeByArray hash[array[idx]], array, newValue, ++idx 

0

初期値を設定する最も信頼できる方法は、render(){}に加えてcomponentDidMount(){}を使用することです。

... 
componentDidMount(){

    const {nameFirst, nameSecond, checkedStatus} = this.props;

    document.querySelector('.nameFirst').value          = nameFirst;
    document.querySelector('.nameSecond').value         = nameSecond;
    document.querySelector('.checkedStatus').checked    = checkedStatus;        
    return; 
}
...

要素を破棄して新しい要素に置き換えるのは簡単です。

<input defaultValue={this.props.name}/>

このような:

if(document.querySelector("#myParentElement")){
    ReactDOM.unmountComponentAtNode(document.querySelector("#myParentElement"));
    ReactDOM.render(
        <MyComponent name={name}  />,
        document.querySelector("#myParentElement")
    );
};

このバージョンのunmountメソッドも使用できます。

ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);

4
ここで自分でDOMを操作しているのですが、反応はそれほど大きくありません。
Devashish

0

パラメータ「placeHolder」に値を指定します。例えば ​​:-

 <input 
    type="text"
    placeHolder="Search product name."
    style={{border:'1px solid #c5c5c5', padding:font*0.005,cursor:'text'}}
    value={this.state.productSearchText}
    onChange={this.handleChangeProductSearchText}
    />
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.