web-dev-qa-db-ja.com

React input defaultValueは状態で更新されません

私は反応するシンプルなフォームを作成しようとしていますが、データをフォームの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}
79

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} />

また、実際に値を「プレースホルダーテキスト」に設定するので、プレースホルダーテキストを渡したくありません。未定義で、nilが値を本質的にdefaultValueに変えるため、空白の値を入力に渡す必要があります。 https://facebook.github.io/react/tips/controlled-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を使用して送信時に値を取得することもできます。そのルートに行く場合、データを取得し、それを小道具としてフォームに渡すための外部シェルコンポーネント(通常は高次コンポーネントと呼ばれる)を使用することをお勧めします。

全体として、反応ドキュメントを最後まで読み、いくつかのチュートリアルを行うことをお勧めします。そこにはたくさんのブログがあり、 http://www.egghead.io にはいくつかの良いチュートリアルがありました。私のサイトにも何かあります http://www.openmindedinnovations.com

59
Blaine Hatab

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

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

更新:これは賛成票を得るので、コンテンツの読み込み中にdisabledまたはreadonlyの小道具を適切に用意する必要があると言わなければならないので、UXエクスペリエンスが低下することはありません。

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

49
TryingToImprove

最善のソリューションではないかもしれませんが、次のようなコンポーネントを作成して、コードのどこでも再利用できるようにします。デフォルトですでに反応しているといいのですが。

<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 
2

初期値を設定する最も信頼できる方法は、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")
    );
};

このバージョンのアンマウント方法も使用できます。

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

パラメーター「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}
    />
0
Manas Gond