react的value和defaultValue之间的困惑

跟着网上各种教程入门react,决定做一个todolist来加深对react的理解
因为之前学过一阵子vue,用vue做的todolist直接把最终的效果图拿来重新用react做
图片描述

上图就是用vue做的
说一下这个todo具备的功能:
1.输入框输入后直接按回车或者点击新增按钮时,将文本添加到下面展示列表部分。
2.每项前的checkbox勾选后文本部分标记已完成并且不能编辑
3.每项鼠标划过在最右端出现删除的'x'点击可以删除该项
4.文本部分用的input[type='text'],点击文本可以修改文本。当发生失焦事件或者回车按下事件时,判断与之前的文本是否一致,不一致弹出确认框,确认框点击确认进行修改,点击取消还原成之前的文本内容。

问题出现在,用react做的时候,input[type='text']的属性用value必须得配合onChange,但是这一块我不会做,
如果用defaultValue出现一个情况就是,不点击删除确认的话,显示没问题,但是点击删除一项时,defaultValue不会再次渲染,所以删除后的列表显示的文本内容还是删除之前的内容。
希望哪位大神耐心看一下我的代码,帮我一下~

我也希望有对我代码优化改进之类的建议

贴一下改动后的代码:

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

//创建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事件
现在可以达到我想要的功能效果了

虽然功能实现了,但是我觉得我的代码看着很混乱,有些代码感觉很冗余但是又不知道该如何优化提炼,比如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':''}/>

这部分,我添加了好多事件,这一块是否可以去掉不必要的代码,或者这些事件所调用的方法是否可以整合去写,再有 每个事件我想传递当前对象作为参数,而我写的是.bind(null,index)传递的是该对象在数组的索引值

阅读 5.6k
1 个回答

既然用defaultValue,表明你这个组件是个非受控组件,无法人为控制value。而defaultValue只能作用一次

解决方案就是把它变成受控组件,至于你说的不会用onChange,这个可以搜一下,不难

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏