setState更改后输入值未更新...增量和减量 [英] Input value not Updating after setState change... Increment and Decrement
问题描述
我有这些递增和递减按钮,它们将相加或相减.我将从props中导入默认值,然后希望允许用户进行调整.因此,当我为适当的组更新状态(系列)时,似乎更新就很好了……但是输入值在HTML中仍然为"14",尽管我将其设置为 value = {this.state [mppt_number] .series}
.状态更改后,它是否应该自动更新?
这可能与[mppt_number]变量有关....如果在创建元素时mppt_number为= 1,则值将不是 value = {this.state [1] .series}
和 value = {this.state [2] .series}
的第二次创建?我不知道...我对此有些陌生.
import从'react'导入React;//从'./Calculator'导入{defaultValues};//将会作为道具传递给它,以简化测试.范围是1到Nconst defaultValues = {1:{array_isc:10.88,array_power:3834.5999999999995,array_vmp:497,array_voc:584.3312579999999,系列:14字符串:1},2:{array_isc:9.00,array_power:3834.5999999999995,array_vmp:600,array_voc:584.3312579999999,系列:12字符串:1},}const mppt = 2工具扩展了React.Component {构造函数(道具){超级(道具)this.state = {stringSizingElement:null,}this.handleStringChange = this.handleStringChange.bind(this)this.stringSizingElementHelper = this.stringSizingElementHelper.bind(this)}componentDidMount(){让s = {};for(让i = 1; i< = mppt; i ++){s [`$ {i}`] = defaultValues [i];}this.setState((prevState)=>({... s}),()=> {console.log(this.state);this.createStringSizingElementDefaults()});}//为每个MPPT创建此元素mppt_number次stringSizingElementHelper(mppt_number){让stringSizing =(< div className ='容器'id = {mppt_number}键= {mppt_number}>< div className =行对齐内容中心">< label> MPPT {mppt_number}</label></div>< div className =行对齐内容中心";id = {`$ {mppt_number}`}>< div className ="col-4">< label className ='container'>系列</label>< div className = {`输入组容器$ {`$ {mppt_number}`}`}>< button className ='btn btn-primary decrement'onClick = {this.handleSeriesChange}>-</button>< input className =表单控制系列";min ="0"名称=数量"值= {this.state [mppt_number] .series} type ="number"禁用/>< button className ='btn btn-primary gain'onClick = {this.handleSeriesChange}> +</button></div></div></div>< br/>< br/></div>);返回stringSizing;}createStringSizingElementDefaults(){let temp = []//为每个MPPT时间创建每个mppt的元素(1:{},2:{})此数字范围.for(让i = 1; i< = mppt; i ++){const结果= this.stringSizingElementHelper(i)temp.push(结果)}this.setState({stringSizingElement:temp})}handleSeriesChange(e){if(e.target.classList.contains('decrement')){//获取ID以找出正在更改的MPPTconst mppt_number = e.target.parentNode.parentNode.parentNode.parentNode.idvar newValues = {... this.state [mppt_number]}newValues.series = this.state [mppt_number] .series-1this.setState({[mppt_number]:newValues},()=> {console.log(this.state [mppt_number] .series)});}其他if(e.target.classList.contains('increment')){const mppt_number =e.target.parentNode.parentNode.parentNode.parentNode.idvar newValues = {... this.state [mppt_number]}newValues.series = this.state [mppt_number] .series + 1console.log(newValues.series)this.setState({[mppt_number]:newValues},()=> {console.log(this.state [mppt_number] .series)})}别的{console.log('错误??')}}使成为(){返回(< div className =临时容器">< div className ="form-row justify-content-center"< label htmlFor ="sel2">< h4>字符串大小调整工具:</h4></label></div>{this.state.stringSizingElement}< br/>< br/>{不明确的}< br/>< br/></div>)}}
帮助这个女孩出去吗?:D
更新:
不再以状态存储这些JSX数组.我在渲染和返回之间创建它们.现在,它不是渲染"或渲染".当它以某种状态存储时?我仍然得到相同的结果.:(
现在我该函数返回数组而不是设置状态.
createStringSizingElementDefaults(){let temp = []//为每个MPPT时间创建每个mppt的元素(1:{},2:{})此数字范围.for(让i = 1; i< = mppt; i ++){const结果= this.stringSizingElementHelper(i)temp.push(结果)}返回温度}
现在在渲染和返回之间创建了这个数组.
render(){让stringSizingElement = this.createStringSizingElementDefaults()返回(< div className =临时容器">< div className ="form-row justify-content-center"< label htmlFor ="sel2">< h4>字符串大小调整工具:</h4</label></div>{stringSizingElement}< br/>< br/>{不明确的}< br/>< br/></div>)}
帮助后的解决方案代码:
import从'react'导入React;//从'./Calculator'导入{defaultValues};//导入'./xxxxx.css';//现在将作为道具传递给它,以简化测试const defaultValues = {1:{array_isc:10.88,array_power:3834.5999999999995,array_vmp:497,array_voc:584.3312579999999,系列:14字符串:1},2:{array_isc:9.00,array_power:3834.5999999999995,array_vmp:600,array_voc:584.3312579999999,系列:12字符串:1},3:{array_isc:1.00,array_power:3834.5999999999995,array_vmp:600,array_voc:584.3312579999999,系列:12字符串:1},}工具扩展了React.Component {构造函数(道具){超级(道具)this.state = {}this.handleStringChange = this.handleStringChange.bind(this)this.handleSeriesChange = this.handleSeriesChange.bind(this)}componentDidMount(){让s = {};Object.keys(defaultValues).map((mppt_number,i)=>(s [`$ {mppt_number}`] = defaultValues [mppt_number]))this.setState((prevState)=>({... s}),()=> {console.log(this.state);});}handleSeriesChange(e){if(e.target.classList.contains('decrement')){//获取ID以找出正在更改的MPPTconsole.log(e.target.parentNode.parentNode.parentNode.parentNode.id)const mppt_number = e.target.parentNode.parentNode.parentNode.parentNode.idvar newValues = {... this.state [mppt_number]}newValues.series = this.state [mppt_number] .series-1this.setState({[mppt_number]:newValues},()=> {console.log(this.state [mppt_number] .series)})}其他if(e.target.classList.contains('increment')){console.log(e.target.parentNode.parentNode.parentNode.parentNode.id)const mppt_number = e.target.parentNode.parentNode.parentNode.parentNode.idvar newValues = {... this.state [mppt_number]}newValues.series = this.state [mppt_number] .series + 1this.setState({[mppt_number]:newValues},()=> {console.log(this.state [mppt_number] .series)})}别的{console.log('错误?也许删除此内容')}}使成为(){返回(< div className =临时容器">< div className ="form-row justify-content-center"< label htmlFor ="sel2">< h4>字符串大小调整工具:</h4</label></div>{Object.keys(this.state).map((mppt_number,i)=>(< div className ='容器'id = {mppt_number}键= {mppt_number}>< div className =行对齐内容中心">< label> MPPT {mppt_number}</label></div>< div className =行对齐内容中心";id = {`$ {mppt_number}`}>< div className ="col-4">< label className ='container'>系列</label>< div className = {`输入组容器$ {`$ {mppt_number}`}`}>< button className ='btn btn-primary decrement'onClick = {this.handleSeriesChange}>-</button>< input className =表单控制系列";min ="0"名称=数量"值= {this.state [mppt_number] .series} type ="number"禁用/>< button className ='btn btn-primary gain'onClick = {this.handleSeriesChange}> +</button></div></div></div>< br/>< br/></div>))}< br/>< br/>{不明确的}< br/>< br/></div>)}}
setStste
仅在新状态下覆盖旧状态,所以:
this.setState({[mppt_number]:newValues},()=> {console.log(this.state [mppt_number] .series)})
不会更新 [mppt_number]:newValues
,而是将状态替换为 {[mppt_number]:newValues}
.如果您想:
<代码> this.setState((oldState)=>({... oldState,... {[mppt_number]:newValues}}),()=> {console.log(this.state [mppt_number] .series)})
以上将起作用.将对象作为 setState
的第一个参数不会导致您期望的更新.赋予功能而不是对象,如上所述.
如果您是React的新手,并且不打算立即使用作业,我建议使用功能组件和挂钩代替类方法.它很简单,并且使代码外观非常好.试试吧!
添加:问题发生在这里:
this.setState({stringSizingElement:temp})
temp
包含React节点,并呈现为初始状态.处于 state
状态的 tringSizingElement
始终是渲染节点,因此变量固定为常量值.
为避免这种情况,请停止将节点存储为状态
.处于 state
状态的节点不在React DOM渲染范围内,这意味着它们未更新其变量.
createStringSizingElementDefaults(){let temp = []for(让i = 1; i< = mppt; i ++){const结果= this.stringSizingElementHelper(i)temp.push(结果)}返回温度}
直接创建 createStringSizingElementDefaults
节点,不会发生此问题.
添加2:
此
是一个麻烦的问题. this
的要点必须与您的想法有所不同.如果调用 this.func.bind(this)
,则 func
中的 this
是固定的.换句话说, this.state
从未更改.因此,即使一遍又一遍地调用 his.createStringSizingElementDefaults()
,结果也一样!避免两个问题?答案之一是再次调用 bind
.但是出于性能原因,不建议这样做.另一个很简单.不访问成员函数中的状态.如果需要,可以将其作为参数.
class示例扩展了React.Component {构造函数(道具){超级(道具)this.state = {清单:[1、2、3],}this.handleAppend = this.handleAppend.bind(this)this.handleRemove = this.handleRemove.bind(this)}handleAppend(){this.setState({someList} => {让s = [... someList]s.push(spmeList [someList.length-1])返回{someList:s}})}handleRemove(){this.setState({someList} => {让s = [... someList]s.pop()返回{someList:s}})}使成为(){返回(< div>< button onClick = {this.handleAppend}>附加</button>< button onClick = {this.handleRemove}>删除</button>{this.state.someList.map(v =>< div> {v}</div>)}}</div>)}}
您可以确认视图仅是决定状态.要更改状态,需要按下按钮并通过 onClick
事件触发 setState
.数据单向流动.这是重要.
总结
-
setState
具有更新功能 - 不包括处于状态的节点
- 请勿在成员函数中访问
this.state
- 如果需要,可以将其作为参数
- 单向保持数据流
如果您有特殊情况,我不会停止使用类样式的组件,但是,我强烈建议您使用功能组件样式.
I have these increment and decrement buttons that will add or subtract. I will import the default values from props and then want to allow the user to make adjustments. So when I update the state (series) for the appropriate group it appears to update just fine... but the input value remains '14' in the HTML although I have it set to value={this.state[mppt_number].series}
. Shouldn't it automatically update when the state changes?
It probably has to do with the [mppt_number] variable.... if mppt_number is =1 when creating the element, wouldn't value be value={this.state[1].series}
and value={this.state[2].series}
for the second time it's created? I don't know... I'm a little new to this.
import React from 'react';
//import { defaultValues } from './Calculator';
//Will be passed in as props just have it here for now to ease testing. This can range from 1 to N
const defaultValues = {
1:{
array_isc: 10.88,
array_power: 3834.5999999999995,
array_vmp: 497,
array_voc: 584.3312579999999,
series: 14,
string: 1,
},
2: {array_isc: 9.00,
array_power: 3834.5999999999995,
array_vmp: 600,
array_voc: 584.3312579999999,
series: 12,
string: 1},
}
const mppt = 2
class Tool extends React.Component{
constructor(props){
super(props)
this.state = {
stringSizingElement : null,
}
this.handleStringChange =this.handleStringChange.bind(this)
this.stringSizingElementHelper = this.stringSizingElementHelper.bind(this)
}
componentDidMount() {
let s = {};
for (let i = 1; i <= mppt; i++) {
s[`${i}`] = defaultValues[i];
}
this.setState((prevState) => ({ ...s }), () => {
console.log(this.state);
this.createStringSizingElementDefaults()
});
}
//Create this element mppt_number of times for each MPPT
stringSizingElementHelper( mppt_number){
let stringSizing = (
<div className='container' id ={mppt_number} key = {mppt_number}>
<div className="row justify-content-center">
<label>MPPT {mppt_number}</label>
</div>
<div className="row justify-content-center" id= {`${mppt_number}`}>
<div className="col-4">
<label className='container'>Series</label>
<div className={`input-group container ${`${mppt_number}`}`} >
<button className ='btn btn-primary decrement' onClick={this.handleSeriesChange} >-</button>
<input className="form-control series" min="0" name="quantity" value={this.state[mppt_number].series} type="number" disabled/>
<button className ='btn btn-primary increment' onClick={this.handleSeriesChange} >+</button>
</div>
</div>
</div>
<br/>
<br/>
</div>
);
return stringSizing;
}
createStringSizingElementDefaults(){
let temp = []
//Creating element for each mppt (1:{} , 2: {}) of times for each MPPT This number ranges.
for (let i=1 ; i <= mppt ; i++) {
const result = this.stringSizingElementHelper(i)
temp.push(result)
}
this.setState({stringSizingElement: temp})
}
handleSeriesChange(e){
if(e.target.classList.contains('decrement')){
//Get ID to Figure out which MPPT Is being changed
const mppt_number = e.target.parentNode.parentNode.parentNode.parentNode.id
var newValues = {...this.state[mppt_number]}
newValues.series = this.state[mppt_number].series - 1
this.setState({[mppt_number] : newValues}, ()=>{
console.log(this.state[mppt_number].series)
});
}else if(e.target.classList.contains('increment')){
const mppt_number =
e.target.parentNode.parentNode.parentNode.parentNode.id
var newValues = {...this.state[mppt_number]}
newValues.series = this.state[mppt_number].series + 1
console.log(newValues.series)
this.setState({[mppt_number] : newValues}, ()=>{
console.log(this.state[mppt_number].series)
})
}else{
console.log('Error??')
}
}
render(){
return(
<div className="temp container">
<div className="form-row justify-content-center">
<label htmlFor="sel2"><h4>String Sizing Tool:</h4>
</label>
</div>
{this.state.stringSizingElement}
<br/>
<br/>
{undefined}
<br/>
<br/>
</div>
)
}
}
Help this girl out? :D
UPDATE:
Didn't store these array of JSX in a state anymore. I create them between render and return now. Now it's not "rendering" when it's stored in a state? I still get the same results. :(
Now I the function returns the array instead of setting a state.
createStringSizingElementDefaults(){
let temp = []
//Creating element for each mppt (1:{} , 2: {}) of times for each MPPT This number ranges.
for (let i=1 ; i <= mppt ; i++) {
const result = this.stringSizingElementHelper(i)
temp.push(result)
}
return temp
}
This array is now created in between render and return.
render(){
let stringSizingElement = this.createStringSizingElementDefaults()
return(
<div className="temp container">
<div className="form-row justify-content-center">
<label htmlFor="sel2"><h4>String Sizing Tool:</h4></label>
</div>
{stringSizingElement}
<br/>
<br/>
{undefined}
<br/>
<br/>
</div>
)
}
SOLUTION CODE AFTER HELP:
import React from 'react';
//import { defaultValues } from './Calculator';
//import './xxxxx.css';
//Will be passed in as props just have it here for now to ease testing
const defaultValues = {
1: {array_isc: 10.88,
array_power: 3834.5999999999995,
array_vmp: 497,
array_voc: 584.3312579999999,
series: 14,
string: 1,
},
2: {array_isc: 9.00,
array_power: 3834.5999999999995,
array_vmp: 600,
array_voc: 584.3312579999999,
series: 12,
string: 1},
3: {array_isc: 1.00,
array_power: 3834.5999999999995,
array_vmp: 600,
array_voc: 584.3312579999999,
series: 12,
string: 1},
}
class Tool extends React.Component{
constructor(props){
super(props)
this.state = {
}
this.handleStringChange =this.handleStringChange.bind(this)
this.handleSeriesChange =this.handleSeriesChange.bind(this)
}
componentDidMount() {
let s = {};
Object.keys(defaultValues).map((mppt_number, i) => (
s[`${mppt_number}`] = defaultValues[mppt_number]
))
this.setState((prevState) => ({ ...s }), () => {
console.log(this.state);
});
}
handleSeriesChange(e){
if(e.target.classList.contains('decrement')){
//Get ID to Figure out which MPPT Is being changed
console.log(e.target.parentNode.parentNode.parentNode.parentNode.id)
const mppt_number = e.target.parentNode.parentNode.parentNode.parentNode.id
var newValues = {...this.state[mppt_number]}
newValues.series = this.state[mppt_number].series - 1
this.setState({[mppt_number] : newValues}, ()=>{
console.log(this.state[mppt_number].series)
})
}else if(e.target.classList.contains('increment')){
console.log(e.target.parentNode.parentNode.parentNode.parentNode.id)
const mppt_number = e.target.parentNode.parentNode.parentNode.parentNode.id
var newValues = {...this.state[mppt_number]}
newValues.series = this.state[mppt_number].series + 1
this.setState({[mppt_number] : newValues}, ()=>{
console.log(this.state[mppt_number].series)
})
}else{
console.log('Error?? Maybe remove this')
}
}
render(){
return(
<div className="temp container">
<div className="form-row justify-content-center">
<label htmlFor="sel2"><h4>String Sizing Tool:</h4></label>
</div>
{Object.keys(this.state).map((mppt_number, i) => (
<div className='container' id ={mppt_number} key = {mppt_number}>
<div className="row justify-content-center">
<label>MPPT {mppt_number}</label>
</div>
<div className="row justify-content-center" id= {`${mppt_number}`}>
<div className="col-4">
<label className='container'>Series</label>
<div className={`input-group container ${`${mppt_number}`}`} >
<button className ='btn btn-primary decrement' onClick={this.handleSeriesChange} >-</button>
<input className="form-control series" min="0" name="quantity" value={this.state[mppt_number].series} type="number" disabled/>
<button className ='btn btn-primary increment' onClick={this.handleSeriesChange} >+</button>
</div>
</div>
</div>
<br/>
<br/>
</div>
))}
<br/>
<br/>
{undefined}
<br/>
<br/>
</div>
)
}
}
setStste
only overwrites old state in new state, so:
this.setState({[mppt_number] : newValues}, ()=>{
console.log(this.state[mppt_number].series)
})
won't update [mppt_number] : newValues
, but replaces state into {[mppt_number] : newValues}
. If you want to:
this.setState((oldState) => ({...oldState, ...{[mppt_number] : newValues}}), ()=>{
console.log(this.state[mppt_number].series)
})
the above will work. Giving an object as a first parameter of setState
doesn't cause updating as you intended. Giving a function Instead of an object, working as above.
If you are new to React, and not for using job immediately, I recommend to use functional components and hooks instead of class method. It's simple and makes outlook of code so good. Try it!
Add: Problem happens here:
this.setState({stringSizingElement: temp})
temp
contains React nodes and is rendered as initial state. tringSizingElement
in state
is always rendered nodes, so variables are fixed as constant values.
To avoid this, stop storing nodes into state
. Nodes in state
are NOT scope of React DOM rendering, it means they are not updated their variables.
createStringSizingElementDefaults(){
let temp = []
for (let i=1 ; i <= mppt ; i++) {
const result = this.stringSizingElementHelper(i)
temp.push(result)
}
return temp
}
To make createStringSizingElementDefaults
nodes directly, this problem won't happen.
Add 2:
this
is a troublesome problem. What this
points must different what you think. If call this.func.bind(this)
, this
in func
is fixed. In other word, this.state
is never changed. So even if calling his.createStringSizingElementDefaults()
over and over again, results are same! To avoid both problems? One of answers is that call bind
again. But it is not recommended in performance reason. Another is simple. Not to access state in member functions. If you want to, give it as parameters.
And to achieve that, "let view depend on state only" helps you. To decide to make view from state only, doesn't cause to confuse. What this means is following example:
class Example extends React.Component{
constructor(props){
super(props)
this.state = {
somelist: [1, 2, 3],
}
this.handleAppend = this.handleAppend.bind(this)
this.handleRemove = this.handleRemove.bind(this)
}
handleAppend(){
this.setState({someList} => {
let s = [...someList]
s.push(spmeList[someList.length - 1])
return {someList: s}
})
}
handleRemove(){
this.setState({someList} => {
let s = [...someList]
s.pop()
return {someList: s}
})
}
render(){
return(
<div>
<button onClick={this.handleAppend}>Append</button>
<button onClick={this.handleRemove}>Remove</button>
{this.state.someList.map(v => <div>{v}</div>)}
</div>
)
}
}
You can confirm that view is decided only state. To change state requires to push button and fire setState
via onClick
event. Data flows one-way. This is IMPORTANT.
Summarize,
setState
takes update function- Do not include nodes in state
- Do not access
this.state
in member funstions- If you want to, give it as parameters
- Keep data flow one-way
If you have a special circumstance, I don't stop to use class-style components, but not, I recommend functional component style strongly.
这篇关于setState更改后输入值未更新...增量和减量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!