JSX语法
JSX中使用变量就是一个{}花括号括起来就行了,对属性而言,使用””双引号赋值字面量或使用{}插入js表达式。
1 2
| const element = <div tabIndex="0"></div>; const element = <img src={user.avatarUrl}></img>;
|
react DOM使用小驼峰的写法。
标签里面如果没有内容,用直接使用</>闭合标签,如果包含许多子元素,用()
1 2 3 4 5 6
| const element = <img src={user.avatarUrl} />; const element = ( <h1 className="greeting"> Hello, world! </h1> );
|
在安全方面,JSX直接使用用户输入的内容,因为react DOM会对输入进行转译,转为字符串避免
。
Babel将JSX转译React.createElement()
函数,函数创建一个对象,这样的对象被称为react元素。
1 2 3 4 5 6 7
| const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } }
|
渲染
元素渲染
react元素是创建开销极小的对象,使用ReactDOM.render()将react元素渲染到dom上
1 2
| const element = <h1>Hello, world</h1>; ReactDOM.render(element, document.getElementById('root'));
|
更新渲染
react元素不可变,代表某个特定时期的ui,更新ui的方法是创建一个全新的元素,并使用ReactDOM.render()。一般来说,ReactDOM.render()只会调用一次。官网使用setInterval定时器来展示时间的更新。
与vue的虚拟DOM类似,也是只更新改变了部分。
组件
组件包含函数组件和class组件
函数组件是指接收的参数是一个对象,并返回一个react元素的组件。
1 2 3
| function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
|
class组件使用es6中的class继承React.Component来定义组件
1 2 3 4 5
| class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
|
渲染
渲染用户自定义组件发生了什么
1 2 3 4 5 6 7
| function Welcome(props) { return <h1>Hello, {props.name}</h1>; } const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById('root') );
|
首先,定义好welcome组件和element,调用ReactDOM.render,对传入的element即<Welcome name=”Sara” />中的自定义组件welcome进行调用,传入{name: ‘Sara’},组件返回<h1>Hello, Sara</h1>。然后在id为root的元素中渲染<h1>Hello, Sara</h1>。
注意自定义组件的名称需要大写,小写会被react认为是原生标签。
组合组件
就是在组件中也可以使用其他组件
提取组件
将耦合的代码变成一个个组件,提高可维护性
props不可更改
也就是说react组件对于传入的props,不能修改它的值
state与生命周期
state与props类似,state是私有并完全受控于当前组件。
将函数组件转为class组件主要注意的是将props替换为this.props。
为class组件添加局部state的步骤:this.props替换成this.state。添加constructor构造函数,为this.state赋初值,通过props来调节父类的构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
ReactDOM.render( <Clock />, document.getElementById('root') );
|
注意JS中为子类添加构造函数需要super开头,并且里面的语句都以;结尾。
生命周期
componentDidMount()
方法会在组件已经被渲染到 DOM 中后运行
componentWillUnmount()
生命周期方法中清除
使用 this.setState()
来时刻更新组件的state
使用state需要注意:
- 不直接修改state,而是使用setState(),在构造函数中唯一给this.state赋值。
1
| this.setState({comment: 'Hello'});
|
- state与props可能是异步更新的,通过让state接收一个函数而不是对象来解决
1 2 3
| this.setState((state, props) => ({ counter: state.counter + props.increment }));
|
数据向下流动
组件无法获知其他组件的状态,state除了拥有和设置了它的组件,其他组件是无法访问的。但是组件也可以选择将state作为props向下传递给子组件
1 2 3
| <h2>It is {this.state.date.toLocaleTimeString()}.</h2> 自定义组件: <FormattedDate date={this.state.date} />
|
子组件是无法知道参数传入的数据是来自父组件的state还是props,还是手动输入的。单向数据流是指state总是属于特定的组件,并且只能影响树中”低于”它们的组件。
事件处理
在react的JSX语法中需要使用{}传入函数作为事件处理函数,并且不能返回false阻止默认行为,必须使用preventDefault。
1 2 3 4 5 6 7 8 9 10 11
| function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); } return ( <a href="#" onClick={handleClick}> Click me </a> ); }
|
为元素添加监听器可以在初始渲染时就添加。
当使用class定义时,常见做法是将事件处理函数声明以一个方法,特别需要注意其中this的绑定
1
| this.handleClick = this.handleClick.bind(this);
|
这样绑定是为了当(子组件)调用时指向的是父组件的this,因为JS中class是不会默认绑定this的,也可以使用箭头函数来绑定。
1 2 3 4 5
| return ( <button onClick={() => this.handleClick()}> Click me </button> )
|
但是存在一个性能问题:当函数作为props传入子组件时,组件有可能进行额外渲染。所以建议在构造函数中绑定this或者使用class fields(实验阶段)
1 2 3
| handleClick = () => { console.log('this is:', this); }
|
传参
向事件处理函数传参,可以使用以下两种方法,箭头函数和通过Function.prototype.bind
1 2
| <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
|
使用箭头函数,事件对象需要显式传递,bind方法的话,事件对象及更多参数通过隐式传递
条件渲染
通过if或条件运算符来选择渲染哪个组件,可以用变量来保存元素,然后直接使用变量
1 2 3 4 5 6 7 8 9 10 11
| if (isLoggedIn) { button = <LogoutButton onClick={this.handleLogoutClick} />; } else { button = <LoginButton onClick={this.handleLoginClick} />; } return ( <div> <Greeting isLoggedIn={isLoggedIn} /> {button} </div> );
|
运算符
与&&
1 2 3 4 5
| {unreadMessages.length > 0 && <h2> You have {unreadMessages.length} unread messages. </h2> }
|
直接在{}中使用&&,当左边为true时右边才会执行,否则不执行。
三目运算同样也可以使用。
阻止组件渲染
通过render方法返回null,让组件不进行渲染。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| function WarningBanner(props) { if (!props.warn) { return null; } return ( <div className="warning"> Warning! </div> ); }
class Page extends React.Component { constructor(props) { super(props); this.state = {showWarning: true}; this.handleToggleClick = this.handleToggleClick.bind(this); }
handleToggleClick() { this.setState(state => ({ showWarning: !state.showWarning })); }
render() { return ( <div> <WarningBanner warn={this.state.showWarning} /> <button onClick={this.handleToggleClick}> {this.state.showWarning ? 'Hide' : 'Show'} </button> </div> ); } }
ReactDOM.render( <Page />, document.getElementById('root') );
|
这段官网的例子中,子组件WarningBanner是否渲染取决于Page中showWarning的值,组件WarningBanner需要对props传入的warn值进行判断,看是否返回null。
列表&key
渲染多个组件
1 2 3 4
| const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((number) => <li>{number}</li> );
|
用map方法来遍历生成5个li标签,然后直接渲染listItems。
直接运行会有警告,原因时缺少key属性
1 2 3 4 5
| const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> );
|
通过toString来作为key,来标识每个元素,通常我们是通过id作为key,不建议用index,因为当顺序发生变化时,使用index会导致性能变差。
key值应该在被使用时才指定,或者在map()中设置。
key在兄弟节点中需要是唯一的,不需要全局唯一。