Входное значение не обновляется после изменения setState… Увеличение и уменьшение

#reactjs #input #setstate

#reactjs #ввод #setstate

Вопрос:

У меня есть эти кнопки увеличения и уменьшения, которые будут добавлять или вычитать. Я импортирую значения по умолчанию из реквизита, а затем хочу разрешить пользователю вносить коррективы. Поэтому, когда я обновляю состояние (серию) для соответствующей группы, кажется, что оно обновляется просто отлично… но входное значение остается ’14’ в HTML, хотя у меня оно установлено value={this.state[mppt_number].series} равным . Разве он не должен автоматически обновляться при изменении состояния?

Вероятно, это связано с переменной [mppt_number] …. если mppt_number = 1 при создании элемента, не будет ли значение равно value={this.state[1].series} и value={this.state[2].series} во второй раз, когда оно создается? Я не знаю… Я немного новичок в этом.

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

Помочь этой девушке? : D

Обновить:

Больше не сохранял этот массив JSX в состоянии. Сейчас я создаю их между рендерингом и возвратом. Теперь это не «рендеринг», когда он хранится в состоянии? Я все еще получаю те же результаты. 🙁

Теперь I функция возвращает массив вместо установки состояния.

 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

    }
 

Теперь этот массив создается между визуализацией и возвратом.

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

КОД РЕШЕНИЯ ПОСЛЕ СПРАВКИ:

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

Ответ №1:

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, а не для немедленного использования job, я рекомендую использовать функциональные компоненты и хуки вместо метода класса. Это просто и делает внешний вид кода таким хорошим. Попробуйте!

Добавить: здесь возникает проблема:

         this.setState({stringSizingElement: temp})
 

temp содержит узлы React и отображается как начальное состояние. tringSizingElement in state всегда отображает узлы, поэтому переменные фиксируются как постоянные значения.

Чтобы избежать этого, прекратите сохранять узлы в state . Узлы в state НЕ являются областью рендеринга React DOM, это означает, что они не обновляют свои переменные.

     createStringSizingElementDefaults(){
        let temp = []
        for (let i=1 ; i <= mppt ; i  ) {
          const  result = this.stringSizingElementHelper(i)
            temp.push(result)
    
        }

        return temp
    }
 

При createStringSizingElementDefaults непосредственном создании узлов этой проблемы не возникнет.

Добавить 2:

this это неприятная проблема. Какие this моменты должны отличаться от того, что вы думаете. Если вызов this.func.bind(this) , this вход func фиксирован. Другими словами, this.state никогда не изменяется. Так что даже если звонить his.createStringSizingElementDefaults() снова и снова, результаты будут те же! Чтобы избежать обеих проблем? Один из ответов — это тот звонок bind снова. Но это не рекомендуется по соображениям производительности. Другой простой. Не обращаться к состоянию в функциях-членах. Если вы хотите, укажите это в качестве параметров.

И для достижения этой цели вам поможет «пусть представление зависит только от состояния». Решение сделать просмотр только из состояния не вызывает путаницы. Что это означает, так это следующий пример:

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

Вы можете подтвердить, что представление определяется только состоянием. Для изменения состояния требуется нажать кнопку и запустить setState через onClick событие. Данные передаются в одностороннем порядке. Это ОЧЕНЬ ВАЖНО.

Подведите итог,

  • setState принимает функцию обновления
  • Не включать узлы в состояние
  • Не получить доступ this.state к функциям участников
    • Если вы хотите, укажите его в качестве параметров
  • Сохраняйте односторонний поток данных

Если у вас особые обстоятельства, я не прекращаю использовать компоненты в стиле класса, но нет, я настоятельно рекомендую стиль функционального компонента.

Комментарии:

1. Я получил те же результаты, что и в прошлый раз. Консоль. журнал показывает, что он обновлен в обеих версиях, моей старой версии и вашей версии, но входной элемент остается value = ’14’. Значение элемента html не обновляется. Не отражает новое состояние.

2. Я пропустил понимание фундаментальной проблемы. Я еще раз прочитаю код, минутку, пожалуйста… Но то, что я сказал, должно помочь вам понять.

3. Привет, я больше не храню массив JSX в состоянии и запускаю createStringSizingElementDefaults при рендеринге. Я обновил свой исходный пост, чтобы уточнить. Я думаю, что вы предложили не сохранять его в состоянии? Но я получаю ту же проблему. Если я сделал то, что вы предложили неправильно, можете ли вы привести мне пример того, что, по вашему мнению, я должен сделать, чтобы исправить это?

4. Извините, то, что я должен сказать, так не хватало. Я скоро перепишу код.

5. Мне нужно будет посмотреть, что означают компоненты в стиле класса и стиль функционального компонента. Это сработало!!! Я обновлю новый код. Это тоже намного чище!