The Road to learn React书籍学习笔记(第二章)
组件的内部状态
组件的内部状态也称为局部状态,允许保存、修改和删除在组件内部的属性,使用ES6类组件可以在构造函数中初始化组件的状态。构造函数只会在组件初始化的时候调用一次
1 class App extends Component{ 2 constructor(props){ 3 super(props); 4 } 5 }
使用ES6编写的组件有一个构造函数时,需要强制地使用 super()
方法, 因为这个 App组件
是 Component
的子类,因为需要在 App组件
声明 extends Component
也可以调用 super(props)
,它会在构造函数中设置 this.props
以供构造函数中访问。否则在构造函数中访问 this.props
,会得到 undefined
例子,组件的初始状态是一个列表
1 const list = [ 2 { 3 title:'React', 4 url: 'https://facebook.github.io/react/', 5 author: 'Jordan Walke', 6 num_comments: 3, 7 points: 4, 8 objectID: 0, 9 } 10 ]; 11 12 class App extends Component{ 13 constructor(props){ 14 super(props); 15 this.state = { 16 list : list, 17 } 18 } 19 }
state
通过使用 this
绑定在类上,因此可以在整个组件中访问到 state
。通过 render()
方法可以映射一个在组件外定义静态列表
1 class App extends Component{ 2 render(){ 3 return( 4 <div className = "App"> 5 {this.state.list.map(item => 6 <div key = {item.objectID}> 7 <span><a href = {item.url}>{item.title}</a></span> 8 <span>{item.author}</span> 9 <span>{item.num_comments}</span> 10 <span>{item.points}</span> 11 </div> 12 ) 13 } 14 </div> 15 ); 16 } 17 }
现在list是㢟的一部分,它在组件的 state
中,可以从list添加、修改、删除列表项。组件的 render
会再次运行,可以简单修改组件内部状态,确保组件重新渲染并且展示从内部状态获取到的正确数据 修改 state
可以使用 setState()
方法来修改
ES6 对象初始化
初始化例子
1 const name = 'Laibh'; 2 const user = { 3 name : name 4 };
当对象中属性名与变量名相同时可以如下操作
1 const name = 'Laibh'; 2 const user = { 3 name 4 };
在应用程序中,列表变量名与状态属性名称共享同一名称
1 //ES5 2 this.state = { 3 list:list 4 } 5 6 //ES6 7 this.state = { 8 list 9 };
简写方法名
1 //ES5 2 var userService = { 3 getUserName :function(user){ 4 return user.firstname + ' ' + user.lastname; 5 } 6 } 7 8 //ES6 9 const userService = { 10 getUserName(user){ 11 return user.firstname + ' ' + user.lastname; 12 } 13 }
计算属性名
1 //ES5 2 var user = { 3 name:'Laibh' 4 } 5 6 //ES6 7 const key = 'name'; 8 const user = { 9 [key] :'Laibh' 10 }
单向数据流
组件中有一些内部的 state
,练习 state
操作的好方式增加一些组件的互动 为列表增加一个删除按钮
1 class App extends Component { 2 3 render() { 4 return ( 5 <div className="App"> 6 {this.state.list.map(item => 7 <div key={item.objectID}> 8 <span> 9 <a href={item.url}>{item.title}</a> 10 </span> 11 <span>{item.author}</span> 12 <span>{item.num_comments}</span> 13 <span>{item.points}</span> 14 <span> 15 <button 16 onClick={() => this.onDismiss(item.objectID)} 17 type="button" 18 > 19 Dismiss 20 </button> 21 </span> 22 </div> 23 )} 24 </div> 25 ); 26 } 27 }
上面类中,onDismiss()
还没有被定义,它通过id来标识哪个应该被删除,此函数绑定到类,就成为了类方法,所以访问它的时候要用 this.onDismiss()
而不是用 onDismiss()
。this
对象是类的实例,为了将 onDismiss()
定义为类方法,需要在构造函数中绑定它。并定义它的逻辑功能
1 class App extends Component { 2 3 constructor(props) { 4 super(props); 5 6 this.state = { 7 list, 8 }; 9 10 this.onDismiss = this.onDismiss.bind(this); 11 } 12 onDismiss(id) { 13 ... 14 } 15 16 render() { 17 ... 18 } 19 }
可以使用JavaScript内置的 filter
方法来删除列表的一项,它会遍历整个列表,通过条件来过滤,匹配的返回 true
并留在列表中
1 onDismiss(id){ 2 const updateList = this.state.list.filter(function isNotId(item){ 3 return item.objectID !== id 4 }); 5 }
或者一行箭头函数
1 onDismiss(id){ 2 const updateList = this.state.list.filter(item => item.objectID !== id); 3 }
接着要更新数据
this.setState({list:updateList});
绑定
类不会自动绑定 this
到实例上 需要自己绑定
1 constructor() { 2 super(); 3 4 this.onClickMe = this.onClickMe.bind(this); 5 }
类的绑定方法也有人写在其他地方,例如render()函数中
1 render(){ 2 return( 3 <button onClick = {this.onClickMe.bind(this)}>Click Me</button> 4 ) 5 }
但是应该避免这样使用,因为它会在每次 render()
方法执行的时绑定类方法。组件每次更新都会导致性能消耗,当在构造函数中绑定的时候,绑定只会在组件实例化时运行一次,这样是一个更好的方式
另外有一些人剔除在构造函数中定义业务逻辑类方法
1 constructor(){ 2 super(); 3 this.onClick = () =>{ 4 conlose.log(this); 5 } 6 }
这样随着时间推移会让构造函数变得混乱,避免使用。构造函数的目的只是实例化类以及所有的属性
事件处理
1 <button 2 onClick={() => this.onDismiss(item.objectID)} 3 type="button"> 4 Dismiss 5 </button>
当传递一个参数到类的方法,需要将它封装到另一个(箭头)函数中,由于要传递给事件处理器使用,因为它必须是一个函数,而下面的这个代码不会工作。因为类方法会在浏览器中打开程序时候立即执行
1 <button onClick = {this.onDismiss(item.objectID)}> 2 Dismiss 3 </button>
倘若写成
1 <button onClick = {onDismiss}> 2 Dismiss 3 </button>
就不会立即执行,但是需要传参数,所以就不这么用
另外的一个解决方案是在外部定义一个包装函数,并且只将定义的函数传递给处理程序。因为需要访问特定的列表项,所以它必须位于 map
函数块的内部
1 class App extends Component{ 2 render(){ 3 return( 4 <div className = "App"> 5 {this.state.list.map(item => 6 const onHandldDismiss = () => 7 this.onDismiss(item.objectID); 8 9 return( 10 <div key={item.objectID}> 11 <span> 12 <a href={item.url}>{item.title}</a> 13 </span> 14 <span>{item.author}</span> 15 <span>{item.num_comments}</span> 16 <span>{item.points}</span> 17 <span> 18 <button 19 onClick={onHandleDismiss} 20 type="button" 21 > 22 Dismiss 23 </button> 24 </span> 25 </div> 26 ) 27 )} 28 </div> 29 ) 30 } 31 }
在事件处理程序中使用箭头函数对性能会有影响
和表单互动
1 const list = [{ 2 title: 'React', 3 url: 'https://facebook.github.io/react', 4 author: 'Jordan Walke', 5 num_comments: 3, 6 points: 4, 7 objectID: 0, 8 }, { 9 title: 'Redux', 10 url: 'https://github.com/reactjs/redux', 11 author: 'Dan Abramov, Andrew Clark', 12 num_comments: 2, 13 points: 5, 14 objectID: 1, 15 }]; 16 const isSearched = searchText => item => item.title.toLowerCase().includes(searchText.toLowerCase()); 17 class FormP extends Component{ 18 constructor(props){ 19 super(props); 20 this.state = {list,searchText:''}; 21 this.onSearchChange = this.onSearchChange.bind(this); 22 this.onDismiss = this.onDismiss.bind(this); 23 } 24 onSearchChange(e){ 25 this.setState({searchText:e.target.value}); 26 } 27 onDismiss(id){ 28 console.log(this); 29 const updateList = this.state.list.filter(item => 30 item.objectID !== id 31 ); 32 this.setState({list:updateList}); 33 } 34 render(){ 35 return( 36 <div className = "FormP"> 37 <form> 38 <input type="text" onChange = {this.onSearchChange}/> 39 </form> 40 {this.state.list.filter(isSearched(this.state.searchText)).map( 41 item => 42 <div key = {item.objectID}> 43 <span> 44 <a href= {item.url}>{item.title}</a> 45 </span> 46 <span>{item.author}</span> 47 <span>{item.num_comments}</span> 48 <span>{item.points}</span> 49 <span> 50 <button onClick = {()=>this.onDismiss(item.objectID)}>Dismiss</button> 51 </span> 52 </div> 53 )} 54 </div> 55 ) 56 } 57 } 58 59 export default FormP;
ES6 解构
在JavaScript ES6 中有一个更方便的方法来访问对象和数组的属性,叫做解构。
1 const user = { 2 firsname : 'L', 3 lastname : 'binhong' 4 } 5 6 //ES5 7 var firstname = user.firstname; 8 var lastname = user.lastname; 9 10 console.log(firstname + '' +lastname); 11 12 //ES6 13 const {firstname, lastname} = user; 14 conlose.log(firstname + '' +lastname);
在JavaScript ES5中每次访问对象的属性都需要额外添加一行代码,但是ES6中就可以在一行中进行,可读性最好的方法就是将对象解构成多个属性时使用多行 对于数组也可以使用解构,可以保持代码的可读性
1 const user = ['1','2','3']; 2 const [ 3 a, 4 b, 5 c 6 ] = user; 7 8 console.log(a,b,c); //1,2,3
受控组件
表单元素 <input>
/<select>
/<textarea>
会以元素HTML的形式保存它们自己的状态,一旦有人从外部做了修改,就会修改内部的值,在React中这被称为不受控组件,因为它们自己处理状态,在React中,我们得把它们变成 受控元素
1 class App extends Componet{ 2 render(){ 3 const {searchText,list} = this.state; 4 return( 5 <div className = "App"> 6 <form> 7 <input 8 type = "text" 9 value = {searchText} 10 onChange = {this.onSearchChange} 11 ></input> 12 </form> 13 </div> 14 ) 15 } 16 }
现在输入框的单项数据流循环是自包含的,组件内部状态是输入框的唯一数据来源
拆分组件
用于搜索的输入组件和一个用于展示的列表组件
1 class App extends Componet{ 2 render(){ 3 const {searchText,list} = this.state; 4 return( 5 <div className = "App"> 6 <Search /> 7 <Table /> 8 </div> 9 ) 10 } 11 }
在组件传递属性并在组件中使用,App组件需要传递本地状态 state
托管的属性和它自己的类方法
1 class App extends Component{ 2 render(){ 3 const {searchText,list} = this.state; 4 return( 5 <div> 6 <Search 7 value = {searchText} 8 onChange = {this.onSearchChange} /> 9 <Table 10 list = {list} 11 pattern = {searchText} 12 onDismiss = {this.onDismiss} /> 13 </div> 14 ) 15 } 16 }
Search组件
1 class Search extends Component{ 2 render(){ 3 const {value,onChange} = this.props; 4 return( 5 <form> 6 <input 7 type = "text" 8 value = {value} 9 onChange = {onChange} /> 10 </form> 11 ) 12 } 13 }
Table组件
1 class Table extends Component{ 2 render(){ 3 const {list,pattern.onDismiss} = this.props; 4 return( 5 <div> 6 {list.filter(isSearched(pattern)).map(item => 7 <div key = {item.objectID}> 8 <span><a href = {item.url}>{item.title}</a></span> 9 <span>{item.title}</span> 10 <soan>{item.points}</span> 11 <span>{item.num_comments}</span> 12 <button onClick ={() => onDismiss(item.objectID)}> 13 Dismiss 14 </button> 15 </div> 16 )} 17 </div> 18 ) 19 } 20 }
可组合组件
在 props
对象中还有一个小小的属性可以供使用: children
属性.通过这个属性可以将元素从上层传递到组件中,这些元素对组件来说是未知的,但是却为组件互相结合提供了可能性、 下例中,将一个文本作为子元素传递到Search组件中
1 class App extends Component{ 2 render(){ 3 const {searchText,list} = user; 4 return( 5 <div> 6 <Search 7 value = {searchText} 8 onChange = {this.onSearchChange} /> 9 ) 10 } 11 }
在 Search
组件可以从 props
对象中解构出 children
属性,就可以指定它应该在哪里显示
1 class Search extends Component{ 2 render(){ 3 const {value,onChange,children} = this.props; 4 return( 5 <form> 6 {children} 7 <input 8 type = "text" 9 value = {value} 10 onChange = {onChange} /> 11 } 12 </form> 13 ) 14 } 15 }
可复用组件
可复用和可组合组件帮助理解合理的组件分层,它们是React视图层的基础
1 class Button extends Component{ 2 render(){ 3 const {onClick,className,children} = this.props; 4 return( 5 <button 6 onClick = {onClick} 7 className = {className} 8 type = "button"> 9 {children} 10 </button> 11 ) 12 } 13 }
Button组件拥有单一可信数据源,一个Button组件可以立即重构所有button。一个Button组件统治了所有button
<Button onclick = {() => onDismiss(item.objectID)}>Dissmiss</Button>
函数式无状态组件(function stateless componenet)
这类组件就是函数,它们接受一个输入并返回一个输出。输入时props,输出就是一个普通的JSX组件实例。函数式无状态组件是函数,并且它们没有本地状态。不能通过 this.state
或者是 this.setState()
来访问或者更新状态,因为这里没有 this
对象,此外,它也没有生命周期方法。 constructor()
与 render()
就是其中两个。 constructor
在一个组件的生命周期只执行一次,而 render()
方法只会在最开始执行一次
ES6 类组件
在类的定义中,它们继承自 React
组件。 extend
会注册所有的生命周期方法,只要在 React component API
中,都可以在组件中使用。通过这种方式,可以使用 render()
方法,此外还可以通过使用 this.state
和 this.setState()
来存储和操作 state
把上例的Search组件重构为一个函数式无状态组件
1 function Search(props){ 2 const {value,onChange,children} = props; 3 return{ 4 <form> 5 {children}<input 6 type = "text" 7 value = {value} 8 onChange = {onChange} /> 9 </form> 10 } 11 }
或者将参数放在函数里面
1 function Search({value,onChange,children}){ 2 return{ 3 <form> 4 {children}<input 5 type = "text" 6 value = {value} 7 onChange = {onChange} /> 8 </form> 9 } 10 }
然后ES6 箭头函数一波
1 const Search = ({value,onChange,children}) => 2 <form> 3 {children}<input 4 type = "text" 5 value = {value} 6 onChange = {onChange} /> 7 </form>
如果想在ES6箭头函数写法中做些事情的话 可以这样写
1 const Search = ({value,onChange,children}) => { 2 3 //do something 4 return( 5 <form> 6 {children}<input 7 type = "text" 8 value = {value} 9 onChange = {onChange} /> 10 </form> 11 ) 12 }
给组件声明样式
可以复用 src/App.css
或者 src/index.css