React输入defaultValue不会随状态更新

Nee*_*iya 84 javascript forms 2-way-object-databinding coffeescript reactjs

我正在尝试使用react创建一个简单的表单,但是在数据正确绑定到表单的defaultValue时遇到困难.

我正在寻找的行为是这样的:

  1. 当我打开我的页面时,文本输入字段应该填入我的数据库中的AwayMessage文本.那就是"示例文本"
  2. 理想情况下,如果我的数据库中的AwayMessage没有文本,我想在Text输入字段中有一个占位符.

但是,现在,我发现每次刷新页面时Text输入字段都是空白的.(虽然我在输入中键入的内容确实保存并保持不变.)我认为这是因为输入文本字段的html在AwayMessage为空对象时加载,但在farMessage加载时不刷新.此外,我无法为该字段指定默认值.

为清晰起见,我删除了一些代码(即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>
Run Code Online (Sandbox Code Playgroud)

我的AwayMessage的console.log显示以下内容:

AWAY_MESSAGE Object {}
AWAY_MESSAGE Object {id: 1, company_id: 1, text: "Sample Text", master_toggle: false}
Run Code Online (Sandbox Code Playgroud)

Bla*_*tab 60

defaultValue仅用于初始加载

如果要初始化输入,则应使用defaultValue,但如果要使用state更改值,则需要使用value.我个人喜欢使用defaultValue,如果我只是初始化它然后只是使用refs来提取我提交时的值.关于反应文档的参考和输入的更多信息,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} />
Run Code Online (Sandbox Code Playgroud)

此外,您不希望像往常一样传递占位符文本,因为这实际上会将值设置为"占位符文本".您仍然需要将空值传递给输入,因为undefined和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.

我还建议您阅读组件生命周期并做出反应.https://facebook.github.io/react/docs/component-specs.html.

用修改和加载状态重写

就个人而言,我不喜欢在渲染中执行整个if else else逻辑并且更喜欢在我的状态中使用'loading'并在表单加载之前渲染一个字体很棒的微调器,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>
Run Code Online (Sandbox Code Playgroud)

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

总的来说,我建议一直阅读反应文档并做一些教程.那里有很多博客,http://www.egghead.io有一些很好的教程.我的网站上也有一些东西,http://www.openmindedinnovations.com.

  • 用值替换defaultValue是否安全?我知道defaultValue仅在初始化时加载,但值似乎也这样做. (2认同)
  • @stealthysnacks可以使用值,但现在你必须设置输入的值才能工作.defaultValue只设置初始值,输入将能够改变,但是当使用值时,它现在被"控制" (2认同)

Try*_*ove 53

另一种解决方法是改变key输入.

<input ref="text" key={this.state.awayMessage ? 'notLoadedYet' : 'loaded'} onChange={this.onTextChange} defaultValue={awayMessageText} />
Run Code Online (Sandbox Code Playgroud)

更新:由于这个得到了upvotes,我将不得不说你应该在内容加载时正确地拥有disabledreadonly支持,所以你不要减少ux体验.

是的,它很可能是一个黑客,但它完成了工作.. ;-)

  • 是的,它有一些缺点,但很容易完成工作. (2认同)
  • 这种方法[在 React 博客上推荐](https://reactjs.org/blog/2018/06/07/you-probously-dont-need-driven-state.html#recommendation-filled-uncontrol-component-with -a-key),所以这不是黑客攻击。 (2认同)

Jar*_*red 33

非常简单,使defaultValue和key相同:

<input defaultValue={myVal} key={myVal}/>
Run Code Online (Sandbox Code Playgroud)

这是推荐的方法之一,网址为https://reactjs.org/blog/2018/06/07/you-probously-dont-need-driven-state.html#recommendation-filled-uncontrol-component-with-a-钥匙

  • 其实没那么简单。当用户在输入字段中输入另一个值时,如果您有更新 redux 的 onchange 方法,那么每次击键都会失去焦点。 (2认同)

jer*_*naa 6

要强制 defaultValue 重新渲染,您需要做的就是更改输入本身的键值。这是你如何做的。

<input 
type="text" 
key={myDynamicKey} 
defaultValue={myDynamicDefaultValue} 
placeholder="It works"/>
Run Code Online (Sandbox Code Playgroud)

  • 不起作用。您在哪里定义 `{myDynamicKey}` ? (2认同)

Mïc*_*röv 3

也许不是最好的解决方案,但我会制作一个如下所示的组件,这样我就可以在代码中的任何地方重用它。我希望它默认已经处于反应状态。

<MagicInput type="text" binding={[this, 'awayMessage.text']} />
Run Code Online (Sandbox Code Playgroud)

该组件可能如下所示:

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()}
    />`
Run Code Online (Sandbox Code Playgroud)

其中change by array是通过数组表示的路径访问哈希的函数

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 
Run Code Online (Sandbox Code Playgroud)