React输入defaultValue不会随状态更新

时间:2015-05-09 23:27:32

标签: javascript forms reactjs coffeescript 2-way-object-databinding

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

我正在寻找的行为是:

  1. 当我打开页面时,文本输入字段应填入我的数据库中的AwayMessage文本。那是“样本文本”
  2. 理想情况下,如果我的数据库中的AwayMessage没有文本,我希望在文本输入字段中有占位符。
  3. 但是,现在,我发现每次刷新页面时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>
    

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

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

9 个答案:

答案 0 :(得分:57)

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} />

此外,您不想像往常一样传递占位符文字,因为这实际上会将值设置为“占位符文字”。您仍然需要将空值传递给输入,因为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

重写并加载状态

就我个人而言,我不喜欢做整个渲染中的其他逻辑而更喜欢使用“加载”#39;在我的状态下,在表单加载http://fortawesome.github.io/Font-Awesome/examples/之前渲染一个字体真棒微调器。这是一个重写,向您展示我的意思。如果我弄乱了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在提交时获取值。如果你走这条路线,我建议你有一个外壳组件(通常称为高阶组件)来获取数据,然后将它作为道具传递给表单。

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

答案 1 :(得分:47)

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

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

更新: 由于这会得到提升,我将不得不说,在内容加载时你应该正确地拥有disabledreadonly道具,这样你就不会减少ux体验。

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

答案 2 :(得分:2)

也许不是最好的解决方案,但我制作了类似下面的组件,因此我可以在代码中的任何地方重复使用它。我希望它默认已经做出反应。

<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 

答案 3 :(得分:2)

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

<input 
type="text" 
key={myDynamicKey} 
defaultValue={myDynamicDefaultValue} 
placeholder="It works"/>

答案 4 :(得分:1)

设置初始值的最可靠方法是使用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 :(得分: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}
    />

答案 6 :(得分:0)

相关问题

在控件上设置 defaulValue 不会更新 state

反向操作很完美:

  • state 设置为默认值,控件 UI 会正确更新,就像给出了 defaulValue 一样。

代码:

let defaultRole = "Owner";

const [role, setRole] = useState(defaultRole);

useEffect(() => {
    setMsg(role);
});

const handleChange = (event) => {
    setRole(event.target.value );
};

// ----

<TextField
    label="Enter Role"
    onChange={handleChange}
    autoFocus
    value={role}
/>

答案 7 :(得分:0)

  1. 为您的默认值定义一个状态
  2. divkey 道具包围您的输入
  3. 将键值设置为与输入的 defaultValue 相同的值。
  4. 调用您在第 1 步中定义的 setDefaultValue 以重新渲染您的组件

示例:

const [defaultValue, setDefaultValue] = useState(initialValue);

useEffect(() => {
    setDefaultValue(initialValue);
}, false)

return (
    <div key={defaultValue}>
        <input defaultValue={defaultValue} />
    </div>
)

答案 8 :(得分:0)

使用 value 而不是 defaultValue 并使用 onChange 方法更改输入的值。