跟着网上各种教程入门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)传递的是该对象在数组的索引值
既然用
defaultValue
,表明你这个组件是个非受控组件,无法人为控制value
。而defaultValue
只能作用一次解决方案就是把它变成受控组件,至于你说的不会用
onChange
,这个可以搜一下,不难