React input defaultValue không cập nhật trạng thái


94

Tôi đang cố gắng tạo một biểu mẫu đơn giản với phản ứng, nhưng gặp khó khăn khi dữ liệu liên kết đúng với Giá trị mặc định của biểu mẫu.

Hành vi tôi đang tìm kiếm là:

  1. Khi tôi mở trang của mình, trường nhập Văn bản sẽ được điền bằng văn bản của AwayMessage trong cơ sở dữ liệu của tôi. Đó là "Văn bản mẫu"
  2. Lý tưởng nhất là tôi muốn có một trình giữ chỗ trong trường nhập Văn bản nếu AwayMessage trong cơ sở dữ liệu của tôi không có văn bản.

Tuy nhiên, hiện tại, tôi thấy rằng trường nhập Văn bản trống mỗi khi tôi làm mới trang. (Mặc dù những gì tôi nhập vào dữ liệu đầu vào không lưu đúng cách và vẫn tồn tại.) Tôi nghĩ điều này là do html của trường văn bản đầu vào tải khi AwayMessage là một đối tượng trống, nhưng không làm mới khi awayMessage tải. Ngoài ra, tôi không thể chỉ định giá trị mặc định cho trường.

Tôi đã xóa một số mã để rõ ràng (tức là 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 của tôi cho AwayMessage hiển thị như sau:

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

Câu trả lời:


61

defaultValue chỉ dành cho tải ban đầu

Nếu bạn muốn khởi tạo đầu vào thì bạn nên sử dụng defaultValue, nhưng nếu bạn muốn sử dụng trạng thái để thay đổi giá trị thì bạn cần sử dụng giá trị. Cá nhân tôi thích chỉ sử dụng defaultValue nếu tôi chỉ khởi tạo nó và sau đó chỉ sử dụng refs để nhận giá trị khi tôi gửi. Có thêm thông tin về refs và input trên các tài liệu react, https://facebook.github.io/react/docs/forms.htmlhttps://facebook.github.io/react/docs/working-with-the- browser.html .

Đây là cách tôi sẽ viết lại đầu vào của bạn:

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

Ngoài ra, bạn không muốn chuyển văn bản giữ chỗ như bạn đã làm vì điều đó thực sự sẽ đặt giá trị thành 'văn bản giữ chỗ'. Bạn vẫn cần phải chuyển một giá trị trống vào đầu vào vì undefined và nil về cơ bản biến giá trị thành defaultValue. https://facebook.github.io/react/tips/controlled-input-null-value.html .

getInitialState không thể thực hiện cuộc gọi api

Bạn cần thực hiện các cuộc gọi api sau khi getInitialState chạy. Đối với trường hợp của bạn, tôi sẽ làm điều đó trong componentDidMount. Làm theo ví dụ này, https://facebook.github.io/react/tips/initial-ajax.html .

Tôi cũng khuyên bạn nên đọc về vòng đời của thành phần với phản ứng. https://facebook.github.io/react/docs/component-specs.html .

Viết lại với các sửa đổi và trạng thái tải

Cá nhân tôi không thích làm toàn bộ nếu khác thì logic trong kết xuất và thích sử dụng 'tải' ở trạng thái của tôi và hiển thị một con quay tuyệt vời phông chữ trước khi tải biểu mẫu, http://fortawesome.github.io/Font- Tuyệt vời / ví dụ / . Đây là một bài viết lại để cho bạn thấy những gì tôi muốn nói. Nếu tôi đánh dấu tích cho cjsx, đó là bởi vì tôi thường chỉ sử dụng coffeescript như thế này ,.

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>

Điều đó sẽ bao gồm nó. Bây giờ đó là một cách để đi về các biểu mẫu sử dụng trạng thái và giá trị. Bạn cũng có thể chỉ sử dụng defaultValue thay vì value và sau đó sử dụng refs để nhận các giá trị khi bạn gửi. Nếu bạn đi theo con đường đó, tôi khuyên bạn nên có một thành phần vỏ ngoài (thường được gọi là các thành phần bậc cao) để tìm nạp dữ liệu và sau đó chuyển nó vào biểu mẫu dưới dạng đạo cụ.

Nhìn chung, tôi khuyên bạn nên đọc toàn bộ tài liệu về phản ứng và làm một số hướng dẫn. Có rất nhiều blog trên mạng và http://www.egghead.io có một số hướng dẫn hay. Tôi cũng có một số thứ trên trang web của mình, http://www.openmindedinnovations.com .


Chỉ tò mò tại sao không thực hiện tốt các cuộc gọi api ở trạng thái ban đầu? getInitialState được thực thi ngay trước componentDidMount và lệnh gọi API không đồng bộ. Đó là thông thường hơn hay có một lý do khác đằng sau nó?
Mïchael Makaröv

1
Tôi không biết chính xác tôi đã đọc nó ở đâu nhưng tôi biết bạn không đặt lệnh gọi api vào đó. Có một thư viện được tạo ra để giải quyết vấn đề này, github.com/andreypopp/react-async . Nhưng tôi sẽ không sử dụng thư viện đó và chỉ đặt nó trong componentDidMount. Tôi biết rằng trong hướng dẫn về tài liệu phản ứng api cũng gọi trong componentDidMount.
Blaine Hatab

@ MïchaelMakaröv - bởi vì các lệnh gọi api là không đồng bộ, và getInitialState trả về trạng thái đồng bộ. Vì vậy, trạng thái ban đầu của bạn sẽ được thiết lập trước khi hoàn tất cuộc gọi api.
drogon

2
Thay thế defaultValue bằng giá trị có an toàn không? Tôi biết defaultValue chỉ tải khi khởi tạo nhưng giá trị dường như cũng thực hiện điều này.
stealthysnacks

2
@stealthysnacks Bạn có thể sử dụng giá trị nhưng bây giờ bạn phải đặt giá trị đó cho đầu vào hoạt động. defaultValue chỉ đặt giá trị ban đầu và đầu vào sẽ có thể thay đổi nhưng khi sử dụng giá trị nó bây giờ là 'kiểm soát'
Blaine Hatab

59

Một cách khác để sửa lỗi này là thay đổi keyđầu vào.

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

Cập nhật: Vì điều này nhận được sự ủng hộ , tôi sẽ phải nói rằng bạn nên có một disabledhoặc readonlyhỗ trợ đúng cách trong khi tải nội dung, vì vậy bạn không làm giảm trải nghiệm ux.

Và vâng, nó rất có thể là một vụ hack, nhưng nó đã hoàn thành công việc .. ;-)


Naive thực hiện - trọng tâm của một lĩnh vực đầu vào thay đổi trong khi giá trị quan trọng là thay đổi (đi ra ngoài onKeyUp ví dụ)
Arthur Kushman

2
Đúng, nó có một số nhược điểm, nhưng hoàn thành công việc một cách dễ dàng.
TryToImprove,

Đây là thông minh. Tôi đã sử dụng nó selectvới thực tế là keynhư defaultValuevậy value.
Avi

thay đổi giá keytrị đầu vào là chìa khóa để nhận được giá trị mới được phản ánh trong đầu vào. Tôi đã sử dụng nó với type="text"thành công.
Jacob Nelson

3

Có thể không phải là giải pháp tốt nhất, nhưng tôi sẽ tạo một thành phần như bên dưới để tôi có thể sử dụng lại nó ở mọi nơi trong mã của mình. Tôi ước nó đã được phản ứng theo mặc định.

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

Thành phần có thể trông giống như:

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()}
    />`

Trong đó thay đổi theo mảng là một hàm truy cập hàm băm bằng một đường dẫn được biểu thị bằng một mảng

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

Cách đáng tin cậy nhất để đặt các giá trị ban đầu là sử dụng componentDidMount () {} ngoài render () {}:

... 
componentDidMount(){

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

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

Bạn có thể thấy dễ dàng khi phá hủy một phần tử và thay thế nó bằng phần tử mới với

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

như thế này:

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

Bạn cũng có thể sử dụng phiên bản này của phương pháp ngắt kết nối:

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

4
Bạn đang tự mình thao túng DOM ở đây .. đó không phải là một KHÔNG KHÔNG lớn trong phản ứng?
Devashish

0

Cung cấp giá trị cho tham số "placeHolder". Ví dụ :-

 <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}
    />
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.