ตอบสนองอินพุต defaultValue ไม่อัปเดตตามสถานะ


100

ฉันกำลังพยายามสร้างแบบฟอร์มง่ายๆด้วยการตอบสนอง แต่ประสบปัญหาในการทำให้ข้อมูลเชื่อมโยงกับ defaultValue ของฟอร์มอย่างถูกต้อง

พฤติกรรมที่ฉันกำลังมองหาคือ:

  1. เมื่อฉันเปิดหน้าของฉันช่องป้อนข้อความควรจะเต็มไปด้วยข้อความ AwayMessage ของฉันในฐานข้อมูลของฉัน นั่นคือ "ข้อความตัวอย่าง"
  2. ตามหลักการแล้วฉันต้องการมีตัวยึดตำแหน่งในช่องป้อนข้อความหาก AwayMessage ในฐานข้อมูลของฉันไม่มีข้อความ

อย่างไรก็ตามตอนนี้ฉันพบว่าช่องป้อนข้อความว่างทุกครั้งที่รีเฟรชหน้า (แม้ว่าสิ่งที่ฉันพิมพ์ลงในอินพุตจะบันทึกอย่างถูกต้องและคงอยู่) ฉันคิดว่านี่เป็นเพราะ html ของฟิลด์ข้อความอินพุตโหลดขึ้นเมื่อ AwayMessage เป็นวัตถุว่างเปล่า แต่จะไม่รีเฟรชเมื่อโหลด 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>

console.log สำหรับ AwayMessage ของฉันแสดงสิ่งต่อไปนี้:

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

คำตอบ:


65

defaultValue ใช้สำหรับการโหลดครั้งแรกเท่านั้น

หากคุณต้องการเริ่มต้นอินพุตคุณควรใช้ defaultValue แต่ถ้าคุณต้องการใช้สถานะเพื่อเปลี่ยนค่าคุณต้องใช้ค่า โดยส่วนตัวแล้วฉันชอบใช้ 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} />

นอกจากนี้คุณไม่ต้องการส่งผ่านข้อความตัวยึดเหมือนที่คุณทำเพราะจะตั้งค่าเป็น 'ข้อความตัวยึดตำแหน่ง' คุณยังคงต้องส่งค่าว่างไปยังอินพุตเนื่องจากไม่ได้กำหนดและศูนย์จะเปลี่ยนค่าเป็น defaultValue เป็นหลัก https://facebook.github.io/react/tips/controlled-input-null-value.html

getInitialState ไม่สามารถโทร api ได้

คุณต้องทำการโทร api หลังจากรัน getInitialState สำหรับกรณีของคุณฉันจะทำใน componentDidMount ทำตามตัวอย่างนี้https://facebook.github.io/react/tips/initial-ajax.html

ฉันขอแนะนำให้อ่านวงจรชีวิตของส่วนประกอบด้วยการตอบสนอง https://facebook.github.io/react/docs/component-specs.html

เขียนใหม่ด้วยการปรับเปลี่ยนและสถานะการโหลด

โดยส่วนตัวฉันไม่ชอบทำทั้งหมดถ้าเป็นอย่างอื่นตรรกะในการเรนเดอร์และชอบใช้ 'กำลังโหลด' ในสถานะของฉันและสร้างฟอนต์สปินเนอร์ที่ยอดเยี่ยมก่อนที่ฟอร์มจะโหลดhttp://fortawesome.github.io/Font- น่ากลัว / ตัวอย่าง / . นี่คือการเขียนใหม่เพื่อแสดงให้คุณเห็นว่าฉันหมายถึงอะไร ถ้าฉันทำเห็บสำหรับ cjsx มันเป็นเพราะปกติฉันแค่ใช้กาแฟแบบนี้

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>

ที่ควรจะครอบคลุม ตอนนี้เป็นวิธีหนึ่งในการใช้รูปแบบที่ใช้สถานะและมูลค่า คุณยังสามารถใช้ defaultValue แทนค่าจากนั้นใช้ refs เพื่อรับค่าเมื่อคุณส่ง หากคุณไปเส้นทางนั้นฉันขอแนะนำให้คุณมีส่วนประกอบเปลือกนอก (โดยปกติเรียกว่าส่วนประกอบลำดับสูง) เพื่อดึงข้อมูลแล้วส่งต่อไปยังแบบฟอร์มเป็นอุปกรณ์ประกอบฉาก

โดยรวมแล้วฉันขอแนะนำให้อ่านเอกสารตอบกลับทั้งหมดและทำแบบฝึกหัด มีบล็อกมากมายและhttp://www.egghead.ioมีบทเรียนดีๆ ฉันมีบางสิ่งบนเว็บไซต์ของฉันเช่นกันhttp://www.openmindedinnovations.com


แค่อยากรู้ว่าทำไมการเรียก API ถึงไม่ดีในสถานะเริ่มต้น getInitialState ได้รับการดำเนินการก่อนที่ componentDidMount และการเรียก API จะไม่ตรงกัน ธรรมดากว่านี้หรือมีเหตุผลอื่นอยู่เบื้องหลัง?
MïchaelMakaröv

1
ฉันไม่รู้ว่าอ่านตรงไหน แต่ฉันรู้ว่าคุณไม่ได้ใส่การโทร api ไว้ที่นั่น มีห้องสมุดที่ถูกสร้างขึ้นมาเพื่อจัดการกับมันเป็นgithub.com/andreypopp/react-async แต่ฉันจะไม่ใช้ไลบรารีนั้นและจะวางไว้ใน componentDidMount ฉันรู้ว่าในบทช่วยสอนเกี่ยวกับเอกสารการตอบกลับมีการเรียก api ใน componentDidMount เช่นกัน
Blaine Hatab

@ MïchaelMakaröv - เนื่องจากการเรียก api เป็นแบบ async และ getInitialState จะคืนสถานะพร้อมกัน ดังนั้นสถานะเริ่มต้นของคุณจะถูกตั้งค่าก่อนที่การเรียก API จะเสร็จสมบูรณ์
drogon

2
การแทนที่ defaultValue ด้วยค่านั้นปลอดภัยหรือไม่ ฉันรู้ว่า defaultValue โหลดเฉพาะในการเริ่มต้นเท่านั้น แต่ดูเหมือนว่าค่าจะทำเช่นนี้ด้วย
stealthysnacks

2
@stealthysnacks สามารถใช้ค่าได้ แต่ตอนนี้คุณต้องตั้งค่านั้นเพื่อให้อินพุตทำงานได้ defaultValue เพียงแค่ตั้งค่าเริ่มต้นและอินพุตจะสามารถเปลี่ยนแปลงได้ แต่เมื่อใช้ค่าตอนนี้จะถูก 'ควบคุม'
Blaine Hatab

64

อีกวิธีหนึ่งในการแก้ไขปัญหานี้คือการเปลี่ยนkeyอินพุต

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

อัปเดต: เนื่องจากสิ่งนี้ได้รับการโหวตเพิ่มฉันจะต้องบอกว่าคุณควรมีdisabledหรือreadonlyสนับสนุนอย่างถูกต้องในขณะที่เนื้อหากำลังโหลดดังนั้นคุณจะไม่ลดประสบการณ์ ux

และเออมันน่าจะเป็นแฮ็ค แต่มันก็ทำงานเสร็จ ..


การใช้งานที่ไร้เดียงสา - โฟกัสของฟิลด์อินพุตเปลี่ยนไปในขณะที่ค่าคีย์เปลี่ยนไป (ออกไปที่คีย์อัพเป็นต้น)
Arthur Kushman

2
ใช่มันมีข้อเสียอยู่บ้าง แต่ทำให้งานเสร็จได้อย่างง่ายดาย
TryToImprove

นี่คือความฉลาด ฉันใช้มันสำหรับselectกับkeyเป็นที่เป็นจริงdefaultValue value
Avi

การเปลี่ยน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

วิธีที่น่าเชื่อถือที่สุดในการตั้งค่าเริ่มต้นคือการใช้ componentDidMount () {} นอกเหนือจาก render () {}:

... 
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);

5
คุณกำลังจัดการ DOM ด้วยตัวคุณเองที่นี่ .. นั่นไม่ใช่ NO ใหญ่ในการตอบสนองหรือ?
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.