需求:组件化实现此功能
1. 显示所有todo列表
2. 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本
第一步,定义组件并渲染静态组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="../js/react.development.js"></script> <script src="../js/react-dom.development.js"></script> <script src="../js/prop-types.js"></script> <script src="../js/babel.min.js"></script> </head> <body> <div id="test"></div> </body> </html> <script type="text/babel"> //App根组件 class AppComponent extends React.Component{ render(){ return( <div> <h1>Simplet TODO List</h1> <AddComponent/> <ListComponent/> </div> ) } } //上方添加组件 class AddComponent extends React.Component { render() { return ( <div> <input type="text"/> <button>Add#1</button> </div> ) } } //下方列表组件 class ListComponent extends React.Component { render() { return ( <ul> <li>1111</li> <li>2222</li> <li>3333</li> </ul> ) } } ReactDOM.render(<AppComponent/>,document.getElementById("test")); </script> <!-- 此页面 1,拆分为三个组件 2,使用组件实现静态页面效果 -->
第二步,动态显示初始化数据
注意考虑:数据保存在哪个组件中?
看数据是某个组件需要它(给它),还是某些组件需要它(给包含它们的父组件)
这里,AddComponent要提供数据并且Add按钮后面的数字是数据的长度加1,ListComponent 要显示数据,都需要用到数据,所以数据应该保存在 AppComponent 中
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="../js/react.development.js"></script> <script src="../js/react-dom.development.js"></script> <script src="../js/prop-types.js"></script> <script src="../js/babel.min.js"></script> </head> <body> <div id="test"></div> </body> </html> <script type="text/babel"> //根组件 class AppComponent extends React.Component{ constructor(props){ super(props); //初始化状态 this.state={ todos:['吃饭','睡觉','敲代码'], } } render(){ const {todos} =this.state;//ES6解构赋值的写法,相当于this.state.todos,这里需要接收的是状态 return( <div> <h1>Simplet TODO List</h1> <AddComponent dataNum={todos.length}/> 给添加组件传一个dataNum属性,属性值是数组长度 <ListComponent todos={todos}/> 给列表组件传入todos属性,属性值是组件对象中的数组 </div> ) } } //添加组件 class AddComponent extends React.Component { render() { return ( <div> <input type="text"/> <button>Add#{this.props.dataNum+1}</button> </div> ) } } //添加组件接收dataNum属性并给予类型限制和必要性限制 AddComponent.propTypes = { dataNum:PropTypes.number.isRequired, } //列表组件 class ListComponent extends React.Component { render() { const { todos } = this.props; //ES6解构赋值的写法,这里接收到的是属性 return ( <ul> {todos.map((todo,index)=><li key={index}>{todo}</li>)} </ul> ) } } //列表组件接收todos属性,并给予类型限制和必要性限制 ListComponent.propTypes={ todos:PropTypes.array.isRequired, } ReactDOM.render(<AppComponent/>,document.getElementById("test")); </script>
第三步,做交互,输入一个 todo ,点击Add按钮,这个 todo 要添加到 state 中的 todos 数据中,并更新 todos 状态
考虑:
我们是点击按钮时要改变父组件中的状态,是在子组件中改变父组件的状态,但这是不允许的,因为状态在哪个组件,更新状态的行为就应该在哪个组件,这里的行为就是指 添加todo 这个事件。
方案:
父组件定义函数,通过标签传递给子组件,子组件调用
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Document</title> 7 <script src="../js/react.development.js"></script> 8 <script src="../js/react-dom.development.js"></script> 9 <script src="../js/prop-types.js"></script> 10 <script src="../js/babel.min.js"></script> 11 </head> 12 <body> 13 <div id="test"></div> 14 </body> 15 </html> 16 <script type="text/babel"> 17 18 //根组件 19 class AppComponent extends React.Component{ 20 constructor(props){ 21 super(props); 22 //初始化状态 23 this.state={ 24 todos:['吃饭','睡觉','敲代码'], 25 } 26 this.addData=this.addData.bind(this); //addData()方法是自己定义的,帮它的this绑定到组件对象上 27 } 28 addData(todo){ 29 const {todos}=this.state; 30 todos.unshift(todo);//unshift()向数组的头部添加元素,push()向数组的尾部添加元素 31 //更新状态 32 this.setState({todos}); 33 } 34 render(){ 35 const {todos}=this.state; 36 return( 37 <div> 38 <h1>Simplet TODO List</h1> 39 <AddComponent dataNum={todos.length} addData={this.addData} todos={todos}/> 40 <ListComponent todos={this.state.todos}/> 41 </div> 42 ) 43 } 44 } 45 //添加组件 46 class AddComponent extends React.Component { 47 constructor(props){ 48 super(props); 49 this.addLi=this.addLi.bind(this); //addLi()自己定义的方法,注意绑定this 50 } 51 addLi(){ 52 alert(this.input.value);//获取的输入框中的值 53 const text=this.input.value.trim() //读取输入数据 54 if(!text){ //考虑输入不合法的情况,比如输入的是一个空字符串,就不添加 55 return; 56 } 57 this.props.addData(text);//调用接收到的addData()这个方法,并传入参数(输入框中的值) 58 this.input.value=''; //清除输入框 59 } 60 render() { 61 return ( 62 <div> 63 <input type="text" ref={input=>this.input=input}/> 64 <button onClick={this.addLi}>Add#{this.props.dataNum+1}</button> 65 </div> 66 ) 67 } 68 } 69 AddComponent.propTypes={ 70 dataNum:PropTypes.number.isRequired, 71 addData:PropTypes.func.isRequired,//函数也是一个属性,可以以同样的方式接收 72 todos:PropTypes.array.isRequired, 73 } 74 75 //列表组件 76 class ListComponent extends React.Component { 77 render() { 78 const { todos } = this.props; 79 return ( 80 <ul> 81 {todos.map((todo,index)=><li key={index}>{todo}</li>)} 82 </ul> 83 ) 84 } 85 86 } 87 ListComponent.propTypes={ 88 todos:PropTypes.array.isRequired, 89 } 90 91 ReactDOM.render(<AppComponent/>,document.getElementById("test")); 92 </script>
补充,考虑用户输入的 todo 列表中已经存在就不添加,只有当用户输入的内容在 todos 中没有出现过才添加
在 addLi() 中添加一个判断