Javascript 反应输入默认值不随状态更新

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30146105/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:43:57  来源:igfitidea点击:

React input defaultValue doesn't update with state

javascriptformsreactjscoffeescript2-way-object-databinding

提问by Neeharika Bhartiya

I'm trying to create a simple form with react, but facing difficulty having the data properly bind to the defaultValue of the form.

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

The behavior I'm looking for is this:

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

  1. When I open my page, the Text input field should be filled in with the text of my AwayMessage in my database. That is "Sample Text"
  2. Ideally I want to have a placeholder in the Text input field if the AwayMessage in my database has no text.
  1. 当我打开我的页面时,文本输入字段应填写我的数据库中 AwayMessage 的文本。那就是“示例文本”
  2. 理想情况下,如果我的数据库中的 AwayMessage 没有文本,我希望在文本输入字段中有一个占位符。

However, right now, I'm finding that the Text input field is blank every time I refresh the page. (Though what I type into the input does save properly and persist.) I think this is because the input text field's html loads when the AwayMessage is an empty object, but doesn't refresh when the awayMessage loads. Also, I'm unable to specify a default value for the field.

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

I removed some of the code for clarity (i.e. onToggleChange)

为了清楚起见,我删除了一些代码(即 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>

my console.log for AwayMessage shows the following:

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

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

回答by Blaine Hatab

defaultValue is only for the initial load

defaultValue 仅用于初始加载

If you want to initialize the input then you should use defaultValue, but if you want to use state to change the value then you need to use value. Personally I like to just use defaultValue if I'm just initializing it and then just use refs to get the value when I submit. There's more info on refs and inputs on the react docs, https://facebook.github.io/react/docs/forms.htmland https://facebook.github.io/react/docs/working-with-the-browser.html.

如果要初始化输入,则应使用 defaultValue,但如果要使用 state 更改值,则需要使用 value。我个人喜欢只使用 defaultValue 如果我只是初始化它,然后在我提交时使用 refs 来获取值。关于反应文档的参考和输入的更多信息,https://facebook.github.io/react/docs/forms.htmlhttps://facebook.github.io/react/docs/working-with-the-浏览器.html

Here's how I would rewrite your input:

以下是我将如何重写您的输入:

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

Also you don't want to pass placeholder text like you did because that will actually set the value to 'placeholder text'. You do still need to pass a blank value into the input because undefined and nil turns value into defaultValue essentially. https://facebook.github.io/react/tips/controlled-input-null-value.html.

此外,您不想像以前那样传递占位符文本,因为这实际上会将值设置为“占位符文本”。您仍然需要将一个空白值传递到输入中,因为 undefined 和 nil 本质上将 value 转换为 defaultValue。https://facebook.github.io/react/tips/controlled-input-null-value.html

getInitialState can't make api calls

getInitialState 无法进行 api 调用

You need to make api calls after getInitialState is run. For your case I would do it in componentDidMount. Follow this example, https://facebook.github.io/react/tips/initial-ajax.html.

你需要在 getInitialState 运行后进行 api 调用。对于您的情况,我会在 componentDidMount 中进行。按照这个例子,https://facebook.github.io/react/tips/initial-ajax.html

I'd also recommend reading up on the component lifecycle with react. https://facebook.github.io/react/docs/component-specs.html.

我还建议阅读 react 的组件生命周期。https://facebook.github.io/react/docs/component-specs.html

Rewrite with modifications and loading state

用修改和加载状态重写

Personally I don't like to do the whole if else then logic in the render and prefer to use 'loading' in my state and render a font awesome spinner before the form loads, http://fortawesome.github.io/Font-Awesome/examples/. Here's a rewrite to show you what I mean. If I messed up the ticks for cjsx, it's because I normally just use coffeescript like this, .

就我个人而言,我不喜欢在渲染中执行全部 if else then 逻辑,并且更喜欢在我的状态中使用“加载”并在表单加载之前渲染一个很棒的字体微调器,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>

That should about cover it. Now that is one way to go about forms which uses state and value. You can also just use defaultValue instead of value and then use refs to get the values when you submit. If you go that route I would recommend you have an outer shell component (usually referred to as high order components) to fetch the data and then pass it to the form as props.

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

Overall I'd recommend reading the react docs all the way through and do some tutorials. There's lots of blogs out there and http://www.egghead.iohad some good tutorials. I have some stuff on my site as well, http://www.openmindedinnovations.com.

总的来说,我建议您通读一遍 react 文档并学习一些教程。那里有很多博客,http://www.egghead.io有一些很好的教程。我的网站上也有一些东西,http://www.openmindinnovations.com

回答by TryingToImprove

Another way of fixing this is by changing the keyof the input.

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

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

Update: Since this get upvotes, I will have to say that you should properly have a disabledor readonlyprop while the content is loading, so you don't decrease the ux experience.

更新:由于这得到了赞成,我不得不说你应该在加载内容时正确地拥有 adisabledreadonlyprop,这样你就不会降低 ux 体验。

And yea, it is most likely a hack, but it gets the job done.. ;-)

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

回答by M?chael Makar?v

Maybe not the best solution, but I'd make a component like below so I can reuse it everywhere in my code. I wish it was already in react by default.

也许不是最好的解决方案,但我会制作一个像下面这样的组件,这样我就可以在我的代码中的任何地方重用它。我希望它已经在默认情况下做出反应。

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

The component may look like:

该组件可能如下所示:

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

Where change by array is a function accessing hash by a path expressed by an 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 

回答by Roman

The most reliable way to set initial values is to use componentDidMount(){} in addition to render(){}:

设置初始值最可靠的方法是使用 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; 
}
...

You may find it easy to destroy an element and replacing it with the new one with

你可能会发现销毁一个元素并用新元素替换它很容易

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

like this:

像这样:

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

You can use also this version of unmount method:

您也可以使用此版本的卸载方法:

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

回答by Manas Gond

Give value to parameter "placeHolder". For example :-

给参数“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}
    />