react输入默认值不随状态更新

IT技术 javascript forms reactjs coffeescript 2-way-object-databinding
2021-02-11 17:37:12

我正在尝试使用 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}
6个回答

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

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

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

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

是的,它有一些缺点,但可以轻松完成工作。
2021-03-21 17:37:12
改变key输入的值是获得新值反映在输入中的关键。type="text"成功地使用了它
2021-03-22 17:37:12
在 React 博客上推荐了这种方法,因此它不是黑客。
2021-03-30 17:37:12
这很聪明。我将它用于selectwith keyasdefaultValue实际上是 value.
2021-04-02 17:37:12
幼稚的实现 - 当键值改变时输入字段的焦点改变(例如 onKeyUp )
2021-04-08 17:37:12

defaultValue 仅用于初始加载

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

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

用修改和加载状态重写

就我个人而言,我不喜欢在渲染中完成整个 if else then 逻辑,并且更喜欢在我的状态中使用“加载”并在表单加载之前渲染一个很棒的字体微调器,http://fortawesome.github.io/Font-真棒/例子/这是重写以向您展示我的意思。如果我搞砸了 cjsx 的刻度,那是因为我通常只使用像这样的咖啡脚本,.

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 来获取值。如果你走那条路,我建议你有一个外壳组件(通常称为高阶组件)来获取数据,然后将它作为props传递给表单。

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

我不知道我在哪里读到它,但我知道你没有把 api 调用放在那里。有一个库可以处理它,github.com/andreypopp/react-async但我不会使用那个库,而是将它放在 componentDidMount 中。我知道在关于 reacts 文档的教程中,也在 componentDidMount 中调用了 api。
2021-03-16 17:37:12
@MïchaelMakaröv -- 因为 api 调用是异步的,并且 getInitialState 同步返回状态。因此,您的初始状态将在 api 调用完成之前设置。
2021-03-18 17:37:12
只是好奇为什么在 get 初始状态下调用 api 不好?getInitialState 在 componentDidMount 之前执行,并且 API 调用是异步的。这是更传统的还是背后有另一个原因?
2021-03-26 17:37:12
用 value 替换 defaultValue 是否安全?我知道 defaultValue 仅在初始化时加载,但 value 似乎也这样做。
2021-03-27 17:37:12
@stealthysnacks 可以使用值,但现在您必须为输入设置该值才能工作。defaultValue 只是设置初始值,输入将能够改变,但是当使用值时,它现在是“受控的”
2021-04-13 17:37:12

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

<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 

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

<input 
type="text" 
key={myDynamicKey} 
defaultValue={myDynamicDefaultValue} 
placeholder="It works"/>
不起作用。你在哪里定义{myDynamicKey}
2021-04-11 17:37:12

相关问题

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