鍍金池/ 問答/Linux  HTML/ react的value和defaultValue之間的困惑

react的value和defaultValue之間的困惑

跟著網(wǎng)上各種教程入門react,決定做一個todolist來加深對react的理解
因為之前學(xué)過一陣子vue,用vue做的todolist直接把最終的效果圖拿來重新用react做
圖片描述

上圖就是用vue做的
說一下這個todo具備的功能:
1.輸入框輸入后直接按回車或者點擊新增按鈕時,將文本添加到下面展示列表部分。
2.每項前的checkbox勾選后文本部分標記已完成并且不能編輯
3.每項鼠標劃過在最右端出現(xiàn)刪除的'x'點擊可以刪除該項
4.文本部分用的input[type='text'],點擊文本可以修改文本。當發(fā)生失焦事件或者回車按下事件時,判斷與之前的文本是否一致,不一致彈出確認框,確認框點擊確認進行修改,點擊取消還原成之前的文本內(nèi)容。

問題出現(xiàn)在,用react做的時候,input[type='text']的屬性用value必須得配合onChange,但是這一塊我不會做,
如果用defaultValue出現(xiàn)一個情況就是,不點擊刪除確認的話,顯示沒問題,但是點擊刪除一項時,defaultValue不會再次渲染,所以刪除后的列表顯示的文本內(nèi)容還是刪除之前的內(nèi)容。
希望哪位大神耐心看一下我的代碼,幫我一下~

我也希望有對我代碼優(yōu)化改進之類的建議

貼一下改動后的代碼:

import React, { Component } from 'react';

import './App.less';


//去除兩邊空格
function trim(str){
  return str.replace(/(^\s*)|(\s*$)/g, "");
}

//新增事項組件
class AddPanel extends Component {
  constructor(props){
    super(props);
    this.addhandler = this.addhandler.bind(this);
    /*this.ButtonAddHandler = this.ButtonAddHandler.bind(this);*/
  }
  addhandler(e){
    var ListDataArr = this.props.ListDataArr;
    var inputStr = this.input.value;
    var trimStr = trim(inputStr);
    if(( e.target.type === 'text' && e.keyCode === 13 ) || e.target.type === 'button'){
      if(trimStr){
        ListDataArr.push({
          text:trimStr,
          ischecked: false
        });
        
        this.props.SetInputToListData(ListDataArr);
      }
      this.input.value = '';
    }
  }
  render(){
    return (
      <div className="addpanel clearfix">
        <button type="button" onClick={this.addhandler}>新增</button>
        <div className="addinput">
          <input type="text" placeholder="請輸入添加事項" onKeyDown={this.addhandler} ref={input => this.input=input} />
        </div>
      </div>
    )
  }
}
//顯示代辦事項組件
class ListPanel extends Component {
  constructor(props){
    super(props);
    this.state = {
      inputText: ''
    };
    this.changeChecked = this.changeChecked.bind(this);
    this.modification = this.modification.bind(this);
    this.keydownModification = this.keydownModification.bind(this);
    this.deletehandler = this.deletehandler.bind(this);
    this.textrecord = this.textrecord.bind(this);
    this.bindinputvalue = this.bindinputvalue.bind(this);
  }
  changeChecked(index){
    var ListDataArr = this.props.ListDataArr;
    ListDataArr[index].ischecked = !ListDataArr[index].ischecked;
    this.props.checkhandler(ListDataArr);
  }
  modification(index,e){
    var ListDataArr = this.props.ListDataArr;
    var afterStr = trim(e.target.value);
    if(afterStr === this.state.inputText){
      ListDataArr[index].text = afterStr;
      this.props.checkhandler(ListDataArr);
    }else{
      var conf = window.confirm('確定修改么?');
      if(conf){
        ListDataArr[index].text = afterStr;
        this.props.checkhandler(ListDataArr);
      }else{
        ListDataArr[index].text = this.state.inputText;
        this.props.checkhandler(ListDataArr);
      }
    }
  }
  keydownModification(index,e){
    if(e.keyCode === 13){
      this.modification(index,e);
    }
  }
  deletehandler(index,e){
    var ListDataArr = this.props.ListDataArr;
    var conf = window.confirm('確定刪除么?');
      if(conf){
        ListDataArr.splice(index,1);
        this.props.checkhandler(ListDataArr);
      }
  }
  textrecord(index,e){
    this.setState({
      inputText: e.target.value
    })
    console.log(e.target.value);
  }
  bindinputvalue(index,e){
    var ListDataArr = this.props.ListDataArr;
    ListDataArr[index].text = e.target.value;
    this.props.checkhandler(ListDataArr);
  }
  render(){
    var ListDataArr = this.props.ListDataArr;
    return (
      <div className="listpanel">
        <h3>您的待辦事項</h3>
        <ul>
          {
            ListDataArr.map((item,index) => {
              return (
                <li key={index+1}>
                  <span>
                    <input type="checkbox" onChange={this.changeChecked.bind(null,index)} checked={item.ischecked?true:false}/>
                    {index+1+": "}
                  </span>
                  <div>
                    <input type="text" value={item.text} onChange={this.bindinputvalue.bind(null,index)} onFocus={this.textrecord.bind(null,index)} onBlur={this.modification.bind(this,index)} onKeyDown={this.keydownModification.bind(null,index)} disabled={item.ischecked?true:false} className={item.ischecked?'done':''}/>
                  </div>
                  <i onClick={this.deletehandler.bind(null,index)}></i>
                </li>
              )
            })
          }
        </ul>
      </div>
    );
  }
}

class Statistics extends Component {
  render(){
    var ListDataArr = this.props.ListDataArr,
        total = ListDataArr.length,
        finished = 0,
        nofinished = 0;
        ListDataArr.forEach(function(item,index){
          if(item.ischecked){
            finished++;
          }else{
            nofinished++;
          }
        })
    return (
      <div className="statisticsPanel">
        共: <span className="color01">{total}</span> 個事項, 其中 完成事項: <span className="color02">{finished}</span> 個, 代辦事項: <span className="color03">{nofinished}</span> 個.
      </div>
    );
  }
}

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      ListDataArr: storage.fetch(),
    }
    this.SetInputToListData = this.SetInputToListData.bind(this);
  }
  SetInputToListData(arr){
    arr.map((item,index) =>{
      return (item.id = index);
    });
    this.setState({
      ListDataArr: arr
    });
    storage.save(arr);
  }
  render() {
    console.log(JSON.stringify(this.state.ListDataArr));
    return (
      <div id="app">
        <AddPanel ListDataArr={this.state.ListDataArr} SetInputToListData={this.SetInputToListData}></AddPanel>
        <ListPanel ListDataArr={this.state.ListDataArr} checkhandler={this.SetInputToListData}></ListPanel>
        <Statistics ListDataArr={this.state.ListDataArr}></Statistics>
      </div>
    );
  }
}

//創(chuàng)建localstorage 
var storageName = 'todolist-react';
const storage = {
  fetch(){
    return JSON.parse(localStorage.getItem(storageName) || '[]');
  },
  save(jsondata){
    localStorage.setItem(storageName,JSON.stringify(jsondata));
  }
}



export default App;

按照一樓給的解決思路,把defaultValue換成了value 并添加onChange事件
現(xiàn)在可以達到我想要的功能效果了

雖然功能實現(xiàn)了,但是我覺得我的代碼看著很混亂,有些代碼感覺很冗余但是又不知道該如何優(yōu)化提煉,比如ListPanel組件里的

<input type="text" value={item.text} onChange={this.bindinputvalue.bind(null,index)} onFocus={this.textrecord.bind(null,index)} onBlur={this.modification.bind(this,index)} onKeyDown={this.keydownModification.bind(null,index)} disabled={item.ischecked?true:false} className={item.ischecked?'done':''}/>

這部分,我添加了好多事件,這一塊是否可以去掉不必要的代碼,或者這些事件所調(diào)用的方法是否可以整合去寫,再有 每個事件我想傳遞當前對象作為參數(shù),而我寫的是.bind(null,index)傳遞的是該對象在數(shù)組的索引值

回答
編輯回答
舊城人

既然用defaultValue,表明你這個組件是個非受控組件,無法人為控制value。而defaultValue只能作用一次

解決方案就是把它變成受控組件,至于你說的不會用onChange,這個可以搜一下,不難

2017年12月17日 17:31