反应输入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}

Answers:


61

defaultValue仅用于初始加载

如果要初始化输入,则应使用defaultValue,但如果要使用状态来更改值,则需要使用value。就我个人而言,如果我要初始化它,那么我只想使用defaultValue,然后在提交时使用refs获取值。在react docs上有关于ref和输入的更多信息,https://facebook.github.io/react/docs/forms.htmlhttps://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-很棒/示例/。这是一个重写,以向您展示我的意思。如果我搞砸了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>

那应该掩盖它。现在,这是使用状态和值的形式的一种方式。您也可以只使用defaultValue而不是value,然后在提交时使用refs获取值。如果走那条路线,我建议您有一个外壳组件(通常称为高阶组件)来获取数据,然后将其作为道具传递给表单。

总的来说,我建议您通篇阅读React文档并做一些教程。有很多博客,并且http://www.egghead.io有一些很好的教程。我的网站上也有一些东西,http://www.openmindedinnovations.com


只是好奇为什么在获取初始状态时不擅长api调用?getInitialState在componentDidMount之前执行,并且API调用是异步的。这是更常规的还是背后的另一个原因?
迈克尔·马卡罗夫

1
我不知道该在哪里阅读书,但我知道您没有在其中放置api调用。有一个用于处理它的库github.com/andreypopp/react-async。但是我不会使用该库,而是将其放在componentDidMount中。我知道在reacts文档教程中,也会在componentDidMount中进行api调用。
布莱恩·哈塔布

@MïchaelMakaröv-因为api调用是异步的,并且getInitialState同步返回状态。因此,将在api调用完成之前设置您的初始状态。
drogon

2
将defaultValue替换为value是否安全?我知道defaultValue仅在初始化时加载,但值似乎也可以这样做。
secretthysnacks

2
@stealthysnacks可以使用值,但是现在您必须设置该值才能使输入正常工作。defaultValue只是设置初始值,输入将能够更改,但是现在使用值时它已被“控制”
Blaine Hatab

60

解决此问题的另一种方法是更改key输入的。

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

更新:由于此举获得了好评,所以我不得不说,在内容加载时,您应该适当使用a disabledreadonlyprop,这样就不会降低ux体验。

是的,这很可能是黑客,但是它可以完成工作。;-)


天真的实现-输入字段的焦点在键值更改时更改(例如,
去掉

2
是的,它有一些缺点,但可以轻松完成工作。
TryingToImprov17年

这很聪明。我用了selectkeydefaultValue这实际上是 value
阿维(Avi)

更改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

设置初始值的最可靠方法是除了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}
    />
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.