setState更改后输入值未更新...增量和减量 [英] Input value not Updating after setState change... Increment and Decrement

查看:44
本文介绍了setState更改后输入值未更新...增量和减量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这些递增和递减按钮,它们将相加或相减.我将从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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆