今天我们来一个小练习,最常见的TODO练习,将组件进行组合使用。
首先明确以下3点:
功能界面的组件化编码流程(重要)
- 1、拆分组件:例如把一个整体界面进行拆分,分为一个个单个组件
- 2、实现静态组件:使用组件实现静态页面效果
- 3、实现动态苏建:动态显示初始化数据;实现交互功能,如绑定事件监听
思考?
- 数据应该保存在哪个组件内?
分析:
- 1、看数据是某个组件需要,还是某些组件需要。某个组件需要就保存在某个组件。某些组件需要就保存在这些组件共同的父组件中;
- 2、然后看数据是什么类型;
思考?
- 子组件中改变父组件的状态:不能在子组件中直接改变父组件的状态
- 方法:状态在哪个组件,更新状态的行为(函数)就应该定义在哪个组件
- 子组件去调用父组件的方法
- 父组件定义函数,传递给子组件,子组件进行调用
示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="test1"></div> <div id="test2"></div> <script src="https://cdn.bootcss.com/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.bootcss.com/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.min.js"></script> <script src="../js/prop-types.js"></script> <script type="text/babel"> //Todo List //1、定义组件:将整体组件拆分为3个单个组件 //将<Add />与<List />组件放在<App />组件中,实现一个静态组件 //2、动态显示初始化数据——数据应该保存在哪个组件内 //3、添加交互,监听事件绑定 class App extends React.Component { constructor(props){ super(props) //初始化状态设置 this.state = { todos: ['aaa','bbb','ccc'] } this.addTodo = this.addTodo.bind(this); } addTodo(todo) { const {todos} = this.state; todos.unshift(todo); //更新状态 this.setState({todos}); } render() { const {todos} = this.state;//解构赋值写法 return ( <div> <h1>TODO LIST</h1> <Add count={todos.length} addTodo={this.addTodo}/> <List todos={todos}/> </div> ) } } class Add extends React.Component { constructor(props){ super(props) this.handleAdd = this.handleAdd.bind(this); } handleAdd() { //1、读取输入的数据 const todo = this.inputVal.value.trim() //2、检测合法性,合法则添加 if(!todo){ return } this.props.addTodo(todo);//因为addTodo被放在属性中传过来 this.inputVal.value=''; } render() { return ( <div> <input type="text" ref={input => this.inputVal=input}/> <button onClick={this.handleAdd}>增加 {this.props.count+1}</button> </div> ) } } Add.propTypes = { count: PropTypes.number.isRequired, addTodo:PropTypes.func.isRequired } class List extends React.Component { render() { return ( <ul> { this.props.todos.map((todo,index)=>{return <li key={index}>{todo}</li>}) } </ul> ) } } List.propTypes = { todos: PropTypes.array.isRequired } ReactDOM.render(<App />,document.getElementById('test1')); </script> </body> </html>
小结–组件组合编写的流程(记住流程)
- 1、拆分组件:根据页面结构拆分组件
- 2、实现静态组件:先给组件类指定render(),每个组件都指定。(此时没有动态数据与交互)
- 3、实现动态组件:先初始化数据的动态显示;然后再添加交互,绑定(bind)事件监听。
在组件中收集表单数据
实际工作中,我们常常需要操作表单数据,如向服务端提交表单数据要发送请求,如检验表单数据合法性,等等。。那么这些操作之前,首先我们需要收集到表单数据。
实例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="test1"></div> <script src="https://cdn.bootcss.com/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.bootcss.com/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.min.js"></script> <script src="../js/prop-types.js"></script> <script type="text/babel"> //1、定义类组件 class LoginForm extends React.Component { constructor(props) { super(props); //初始化状态 this.state = { psw : '' } this.handleSubmit = this.handleSubmit.bind(this);//绑定事件侦听 this.handleChange = this.handleChange.bind(this);//绑定事件侦听 } handleSubmit(event) { const name = this.nameInput.value; const {psw} = this.state; alert('username:'+name+' password:'+psw); //阻止事件的默认行为 event.preventDefault(); } //psw失去焦点事件 handleChange(event) { //读取输入值 const psw = event.target.value; //更新psw状态 this.setState({psw}); } render() { return ( <form action="" method="" onSubmit={this.handleSubmit}> <label htmlFor="username">用户名:</label> <input type="text" id="username" ref={input => this.nameInput = input}/> <label htmlFor="psw">密码:</label> <input type="password" id="psw" value={this.state.psw} onChange={this.handleChange}/> <input type="submit" value="Login In"/> </form> ) } } //2、渲染组件 ReactDOM.render(<LoginForm/>,document.getElementById('test1')); </script> </body> </html>
小结
包含表单的组件分为2类:
- 受控组件:表单项输入数据能自动收集成状态state,如password,读状态state
- 非受控组件: 需要时才手动读取表单的数据,如username,需要手动设置
“受控”与“非受控”两个概念,区别在于这个组件的状态是否可以被外部修改。一个设计得当的组件应该同时支持“受控”与“非受控”两种形式,即当开发者不控制组件属性时,组件自己管理状态,而当开发者控制组件属性时,组件该由属性控制。而开发一个复杂组件更需要注意这点,以避免只有部分属性受控,使其变成一个半受控组件。
大多数情况请,推荐使用“受控组件”来实现表单,比较符合React的思想,但是这两者的效率并没有太大的差异。