React 输入 defaultValue 不随状态更新
Posted
技术标签:
【中文标题】React 输入 defaultValue 不随状态更新【英文标题】:React input defaultValue doesn't update with state 【发布时间】:2015-07-20 16:25:43 【问题描述】:我正在尝试使用 react 创建一个简单的表单,但在将数据正确绑定到表单的 defaultValue 时遇到了困难。
我正在寻找的行为是这样的:
-
当我打开我的页面时,文本输入字段应填写我数据库中的 AwayMessage 的文本。那就是“示例文本”
理想情况下,如果我的数据库中的 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
【问题讨论】:
【参考方案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.html 和 https://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 />
更新:
既然这会得到支持,我不得不说你应该在内容加载时正确地拥有一个 disabled
或 readonly
道具,这样你就不会降低用户体验。
是的,这很可能是一个 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】:
-
为您的默认值定义一个状态
用
div
和key
属性包围您的输入
将键值设置为与输入的默认值相同的值。
调用您在第 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/>
【讨论】:
以上是关于React 输入 defaultValue 不随状态更新的主要内容,如果未能解决你的问题,请参考以下文章
React-将输入属性从defaultValue设置为value会触发无限循环
React defaultValue 导致过滤功能的行为与预期不同
React 之form表单selecttextareacheckbox使用
夺命雷公狗-----React---18--value和defaultValue的区别