【问题标题】:React input defaultValue doesn't update with stateReact 输入 defaultValue 不随状态更新
【发布时间】:2015-07-20 16:25:43
【问题描述】:

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

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

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

但是,现在,我发现每次刷新页面时文本输入字段都是空白的。 (尽管我在输入中输入的内容确实正确保存并持续存在。)我认为这是因为输入文本字段的 html 会在 AwayMessage 为空对象时加载,但在 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}

【问题讨论】:

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


    【解决方案1】:

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

    <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 
    

    【讨论】:

      【解决方案2】:

      defaultValue 仅用于初始加载

      如果你想初始化输入那么你应该使用defaultValue,但是如果你想使用state来改变值那么你需要使用value。就我个人而言,如果我只是初始化它,我喜欢只使用 defaultValue,然后在我提交时使用 refs 来获取值。在 React 文档 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 本质上会将 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 逻辑,而是更喜欢在我的状态下使用“加载”并在表单加载之前渲染一个很棒的字体微调器,http://fortawesome.github.io/Font-Awesome/examples/。这是一个重写,向您展示我的意思。如果我弄乱了 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 获取值。如果你走这条路,我建议你有一个外壳组件(通常称为高阶组件)来获取数据,然后将其作为道具传递给表单。

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

      【讨论】:

      • 只是好奇为什么在获取初始状态下进行 api 调用没有好处? getInitialState 在 componentDidMount 之前执行,并且 API 调用是异步的。这是更传统的做法还是另有原因?
      • 我不知道我在哪里读到它,但我知道你没有在那里放 api 调用。有一个库可以处理它,github.com/andreypopp/react-async。但我不会使用那个库,而是将它放在 componentDidMount 中。我知道在 reacts 文档的教程中,componentDidMount 中的 api 调用也是如此。
      • @MïchaelMakaröv --因为 api 调用是异步的,getInitialState 同步返回状态。因此,您的初始状态将在 api 调用完成之前设置。
      • 用值替换 defaultValue 是否安全?我知道 defaultValue 仅在初始化时加载,但 value 似乎也这样做。
      • @stealthysnacks 可以使用 value 但现在您必须设置该值才能使输入正常工作。 defaultValue 只是设置初始值,输入将能够改变,但是当使用 value 时,它​​现在是“受控的”
      【解决方案3】:

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

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

      更新: 既然这会得到支持,我不得不说你应该在内容加载时正确地拥有一个 disabledreadonly 道具,这样你就不会降低用户体验。

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

      【讨论】:

      • 朴素实现 - 输入字段的焦点在更改键值时更改(例如 onKeyUp)
      • 是的,它有一些缺点,但很容易完成工作。
      • 这很聪明。我将它用于select,将key 用作defaultValue,实际上是value
      • 改变输入的key是获得输入反映的新值的关键。我用type="text"成功了。
      • 这种方法是recommended on the React blog,所以它不是黑客。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        相关问题

        在控件上设置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}
        />
        

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

          • 不起作用。你在哪里定义{myDynamicKey}
          【解决方案7】:
          1. 为您的默认值定义一个状态
          2. divkey 属性包围您的输入
          3. 将键值设置为与输入的默认值相同的值。
          4. 调用您在第 1 步中定义的 setDefaultValue 重新渲染您的组件

          示例:

          const [defaultValue, setDefaultValue] = useState(initialValue);
          
          useEffect(() => {
              setDefaultValue(initialValue);
          }, false)
          
          return (
              <div key={defaultValue}>
                  <input defaultValue={defaultValue} />
              </div>
          )
          

          【讨论】:

            【解决方案8】:

            使用value 代替defaultValue 并使用onChange 方法更改输入值。

            【讨论】:

            • 在反应(受控组件)中使用 defaultValue 而不是 value 是有特定原因的
            【解决方案9】:

            非常简单,让defaultValue和key一样:

            <input defaultValue={myVal} key={myVal}/>
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-08-24
              • 2021-07-26
              • 1970-01-01
              • 2020-01-28
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多