प्रतिक्रिया इनपुट defaultValue राज्य के साथ अद्यतन नहीं करता है


94

मैं प्रतिक्रिया के साथ एक सरल रूप बनाने की कोशिश कर रहा हूं, लेकिन डेटा को ठीक से फ़ॉर्म के डिफ़ॉल्टवैल्यू से बाँधने में कठिनाई का सामना करना पड़ रहा है।

जिस व्यवहार की मुझे तलाश है वह यह है:

  1. जब मैं अपना पृष्ठ खोलता हूं, तो पाठ इनपुट फ़ील्ड को मेरे डेटाबेस में मेरे AwayMessage के पाठ के साथ भरना चाहिए। वह "नमूना पाठ" है
  2. आदर्श रूप से मैं पाठ इनपुट क्षेत्र में एक प्लेसहोल्डर रखना चाहता हूं यदि मेरे डेटाबेस में एवमेज़ेज में कोई पाठ नहीं है।

हालाँकि, अभी, मुझे पता चल रहा है कि हर बार जब मैं पृष्ठ को रिफ्रेश करता हूं तो टेक्स्ट इनपुट फ़ील्ड रिक्त होता है। (हालांकि मैं इनपुट में जो टाइप करता हूं वह सही तरीके से सहेजता है और जारी रहता है।) मुझे लगता है कि यह इसलिए है क्योंकि इनपुट टेक्स्ट फील्ड का html लोड होता है जब एवमेज़ेज एक खाली ऑब्जेक्ट होता है, लेकिन जब एमेज़ेज लोड नहीं होता है तो ताज़ा नहीं होता है। साथ ही, मैं फ़ील्ड के लिए एक डिफ़ॉल्ट मान निर्दिष्ट करने में असमर्थ हूं।

मैंने स्पष्टता के लिए कुछ कोड हटा दिए (यानी 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 के लिए मेरा कंसोल .log निम्नलिखित दिखाता है:

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

जवाबों:


61

defaultValue केवल प्रारंभिक भार के लिए है

यदि आप इनपुट को इनिशियलाइज़ करना चाहते हैं तो आपको defaultValue का उपयोग करना चाहिए, लेकिन यदि आप मूल्य को बदलने के लिए स्टेट का उपयोग करना चाहते हैं तो आपको मूल्य का उपयोग करने की आवश्यकता है। व्यक्तिगत रूप से मैं सिर्फ डिफ़ॉल्टवैल्यू का उपयोग करना पसंद करता हूं यदि मैं इसे शुरू कर रहा हूं और फिर जब मैं सबमिट करता हूं तो केवल मूल्य प्राप्त करने के लिए रीफ का उपयोग करता हूं। प्रतिक्रिया डॉक्स, 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} />

इसके अलावा आप अपने जैसे प्लेसहोल्डर टेक्स्ट को पास नहीं करना चाहते हैं क्योंकि यह वास्तव में 'प्लेसहोल्डर टेक्स्ट' के लिए मूल्य निर्धारित करेगा। आपको अभी भी इनपुट में रिक्त मान को पास करने की आवश्यकता है क्योंकि अपरिभाषित और शून्य अनिवार्य रूप से डिफ़ॉल्ट वैल्यू में बदल जाता है। https://facebook.github.io/react/tips/controlled-input-null-value.html

getInitialState एपीआई कॉल नहीं कर सकता

GetInitialState चलाने के बाद आपको api कॉल करना होगा। आपके मामले के लिए मैं इसे कंपोनेंटडाउन में करूंगा। इस उदाहरण का अनुसरण करें, 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


बस उत्सुक है कि प्रारंभिक अवस्था में एपि कॉल करने में कोई भलाई क्यों नहीं है? getInitialState को कंपोनेंटडिमाउंट से ठीक पहले निष्पादित किया जाता है, और API कॉल async है। क्या यह अधिक परंपरागत है या इसके पीछे कोई और कारण है?
मोचेल मकरोव

1
मुझे नहीं पता कि मैं इसे कहाँ पढ़ता हूँ, लेकिन मुझे पता है कि आप वहाँ में एपीआई कॉल नहीं करते हैं। एक पुस्तकालय है जो इससे निपटने के लिए बनाया गया था, github.com/andreypopp/react-async । लेकिन मैं उस लाइब्रेरी का उपयोग नहीं करूंगा और इसे कंपोनेंटडिमाउंट में रखूंगा। मुझे पता है कि रिएक्ट्स पर ट्यूटोरियल में डॉक्यूमेंटडिमाउंट में एपीआई कॉल भी करता है।
ब्लेन हाटाब

@ M @chaelMakaröv -because एपीआई कॉल async हैं, और getInitialState सिंक्रनाइज़ेशन से रिटर्न देता है। आपी कॉल पूरा होने से पहले, आपका प्रारंभिक राज्य स्थापित किया जाएगा।
ड्रगॉन

2
क्या डिफॉल्ट के साथ डिफॉल्ट वैल्यू को बदलना सुरक्षित है? मुझे पता है कि DefaultValue लोड केवल इनिशियलाइज़ेशन पर ही होता है लेकिन वैल्यू भी ऐसा करने लगती है।
stealthysnacks

2
@stealthysnacks मूल्य का उपयोग करना ठीक है, लेकिन अब आपको इनपुट के लिए उस मूल्य को भी काम करना है। defaultValue सिर्फ प्रारंभिक मूल्य निर्धारित करता है और इनपुट बदलने में सक्षम होगा लेकिन मान का उपयोग करते समय यह अब 'नियंत्रित' है
ब्लेन हाटब

60

इसे ठीक करने का एक और तरीका keyइनपुट का परिवर्तन है ।

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

अद्यतन: चूंकि यह उठता है, इसलिए मुझे कहना होगा कि सामग्री लोड होने के दौरान आपको ठीक से होना चाहिए disabledया readonlyप्रचार करना चाहिए , ताकि आप ux अनुभव को कम न करें।

और हाँ, यह सबसे अधिक संभावना है एक हैक, लेकिन यह काम हो जाता है .. ;-)


Naive कार्यान्वयन - कुंजी मूल्य में परिवर्तन करते समय एक इनपुट फ़ील्ड का ध्यान केंद्रित करना (उदाहरण के लिए
onKeyUp पर निकल जाना

2
हाँ, यह कुछ कमियां हैं, लेकिन काम आसानी से हो जाता है।
TryingToImprove

यह स्मार्ट है। मैं के लिए यह प्रयोग किया जाता selectके साथkey रूप मेंdefaultValue जो वास्तव में है value
अवि।

keyइनपुट में परिवर्तन नए मूल्य को इनपुट में परिलक्षित करने के लिए महत्वपूर्ण है। मैंने इसका type="text"सफलतापूर्वक उपयोग किया ।
जैकब नेल्सन

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(){

    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
आप डोम को अपने यहाँ जोड़-तोड़ कर रहे हैं .. क्या प्रतिक्रिया में यह कोई बड़ा नहीं है?
देवाशीष

0

"प्लेसहोल्डर" पैरामीटर को मान दें। उदाहरण के लिए :-

 <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}
    />
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.