快速入门
点击进入——React中文文档
JSX及元素渲染
-
React 是一个 JavaScript 库 。
-
const element = <h1>Hello, world!</h1>;
考虑一下这个变量的声明,它既不是字符串也不是HTML,这就是 JSX ,它是 JavaScrip 的一种扩展语法。我们推荐在 React 中使用这种语法来描述 UI 信息,它具有 JavaScrip 的全部能力,可以生成React “元素”。 -
比起 HTML,JSX 更接近于 JavaScript,所以 React DOM 使用驼峰(camelCase)属性命名约定,而不是HTML属性名称。 例如,
class
在JSX中变为className
,tabindex
变为tabIndex
。 -
元素(Elements)是 React 应用中最小的构建部件,它是构成组建的“材料”,一个元素用于描述你在将在屏幕上看到的内容,例如:
const element = <h1>Hello, world</h1>;
。 -
React 元素是 不可突变(immutable) 的,一旦你创建了一个元素, 就不能再修改其子元素或任何属性。一个元素就像电影里的一帧: 它表示在某一特定时间点的 UI 。更新 UI 的唯一方法是创建一个新的元素, 并将其传入
ReactDOM.render()
方法。但实际上,大多数 React 应用只会调用ReactDOM.render()
一次。更新UI更多的是通过与 状态(state) 和 生命周期 挂钩来实现的。
组件(Components) 和 属性(Props)
-
组件名称总是以大写字母开始。
-
组件必须返回一个单独的根元素。
-
function sum(a, b) { return a + b; }
,这种函数称为 “ 纯函数 ” ,因为它们不会试图改变它们的输入,并且 对于同样的输入,始终可以得到相同的结果 。 反之, 以下是非纯函数, 因为它改变了自身的输入值:function withdraw(account, amount) { account.total -= amount; }
-
所有 React 组件都必须是 纯函数,并禁止修改其自身 props 。
状态(State) 和 生命周期
-
this.props
由 React 本身设定, 而this.state
具有特殊的含义,但如果需要存储一些不用于视觉输出的内容,则可以手动向类中添加额外的字段。 如果在render()
方法中没有被引用, 它不应该出现在state
中。 -
关于 setState() 有三件事是你应该知道的:
-
不要直接修改 state(状态)
例如,这样将不会重新渲染一个组件:
this.state.comment = 'Hello';
正确的做法是用
setState()
来修改state
:this.setState({comment: 'Hello'});
-
state(状态) 更新可能是异步的
React 为了优化性能,有可能会将多个
setState()
调用合并为一次更新。因为this.props
和this.state
可能是异步更新的,你不能依赖他们的值计算下一个state(状态)。例如, 以下代码可能导致 counter(计数器)更新失败:
// 错误 this.setState({ counter: this.state.counter + this.props.increment, }); 复制代码
要弥补这个问题,使用另一种
setState()
的形式,它 接受一个函数而不是一个对象 。这个函数将接收 前一个状态 作为 第一个参数 ,应用 更新时的 props 作为 第二个参数 :// 正确 this.setState((prevState, props) => ({ counter: prevState.counter + props.increment })); 复制代码
-
state(状态)更新会被合并
当你调用
setState()
, React 将合并你提供的对象到当前的状态中。 -
数据向下流动
如果把组件树想像为 props(属性) 的瀑布,所有组件的 state(状态) 就如同一个额外的水源汇入主流,且只能随着主流的方向向下流动。
任何 state(状态) 始终由某个特定组件所有,并且从该 state(状态) 导出的任何数据 或 UI 只能影响树中 “下方” 的组件。
处理事件
- 通过 React 元素处理事件跟在 DOM 元素上处理事件非常相似。但是有一些语法上的区别:
-
React 事件使用驼峰命名,而不是全部小写。
-
通过 JSX , 你传递一个函数作为事件处理程序,而不是一个字符串。
-
在 React 中你不能通过返回
false
来阻止默认行为。必须明确调用preventDefault
。例如,对于纯 HTML ,要阻止链接打开一个新页面的默认行为,可以这样写:<a href="#" onclick="console.log('The link was clicked.'); return false"> Click me </a> 复制代码
在 React 中, 应该这么写:
function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); } return ( <a href="#" onClick={handleClick}> Click me </a> ); } 复制代码
条件渲染
-
在 React 中,你可以创建不同的组件封装你所需要的行为。然后,只渲染它们之中的一些,取决于你的应用的状态。 React 中的条件渲染就和在 JavaScript 中的条件语句一样 。使用 JavaScript 操作符如 if 或者条件操作符来创建渲染当前状态的元素,并且让 React 更新匹配的 UI 。
虽然声明一个变量并使用一个 if 语句是一个有条件地渲染组件的好方法,有时你可能想要使用一个更简短的语法。在 JSX 中有几种内联条件的方法,如下所述。
-
使用逻辑 && 操作符的内联 if 用法
例如当数组
arr
不为空时渲染组件<ShowMessages>
,可以写成{ arr.length>0 && <ShowMessages> } 复制代码
它可以正常运行,因为在 JavaScript 中,
true && expression
总是会评估为expression
,而false && expression
总是执行为false
。 因此,如果条件为true
,则&&
后面的元素将显示在输出中。 如果是false
,React 将会忽略并跳过它。 -
使用条件操作符的内联 If-Else
另一个用于条件渲染元素的内联方法是使用 JavaScript 的条件操作符
If-Else
或者是三目运算符condition ? true : false
。
还要记住,无论何时何地,当条件变得太复杂时,可能是提取组件的好时机。
-
-
防止组件渲染
在极少数情况下,您可能希望组件隐藏自身,即使它是由另一个组件渲染的。为此,返回 null 而不使其渲染输出。
例如在如下代码中:
//方法一: 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(prevState => ({ showWarning: !prevState.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') ); 复制代码
Hide
按钮可以控制Warning
div 的显示隐藏。 同样,以下代码也能达到相同效果://方法二: class Page extends React.Component { constructor(props) { super(props); this.state = {showWarning: true} this.handleToggleClick = this.handleToggleClick.bind(this); } handleToggleClick() { this.setState(prevState => ({ showWarning: !prevState.showWarning })); } render() { return ( <div> <div className="warning" style={{display:this.state.showWarning?'':'none'}}> Warning! </div> <button onClick={this.handleToggleClick}> {this.state.showWarning ? 'Hide' : 'Show'} </button> </div> ); } } ReactDOM.render( <Page />, document.getElementById('root') ); 复制代码
这里没有把
Warning
div 当做组件抽离出来,虽然也能达到相同的效果,但是使用display
只是控制了Warning
div 的显示隐藏,不论如何,Warning
div 其实都已经在页面加载了,消耗了资源;而第一种方法返回null
,却是完完全全的控制了Warning
div 的加载,性能上更胜一筹。PS:从组件的 render 方法返回 null 不会影响组件生命周期方法的触发。 例如,
componentWillUpdate
和componentDidUpdate
仍将被调用。
列表(Lists) 和 键(Keys)
-
首先,先来看一段代码:
const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((numbers) => <li>{numbers}</li> ); ReactDOM.render( <ul>{listItems}</ul>, document.getElementById('root') ); 复制代码
这段代码显示从 1 到 5 的数字列表。
当运行上述代码时,虽然会正确显示出列表,但是会收到一个警告:a key should be provided for list items(应该为列表元素提供一个键)。当创建元素列表时,“key” 是一个你需要包含的特殊字符串属性。 键(Keys) 帮助 React 标识哪个项被修改、添加或者移除了 。数组中的 每一个元素 都应该有一个 唯一不变 的键(Keys)来标识。挑选 key 最好的方式是使用一个在它的同辈元素中不重复的标识字符串。多数情况你可以使用数据中的 ID 作为 keys;当要渲染的列表项中没有稳定的 IDs 时,你可以使用数据项的索引值作为 key 的最后选择,例如:
const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((numbers,index) => <li key={index}>{numbers}</li> ); ReactDOM.render( <ul>{listItems}</ul>, document.getElementById('root') ); 复制代码
-
使用 keys 提取组件 keys 只在数组的上下文中存在意义。例如,如果你提取 一个
ListItem
组件,应该把 key 放置在数组处理的<ListItem />
元素中,不能放在ListItem
组件自身中的<li>
根元素上。例如:
function ListItem(props) { const value = props.value; return ( // 错误!不需要在这里指定 key: <li key={value.toString()}> {value} </li> ); } function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 错误!key 应该在这里指定: <ListItem value={number} /> ); return ( <ul> {listItems} </ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') ); 复制代码
一个好的经验准则是:元素中调用 map() 需要 keys 。
-
keys 在同辈元素中必须是唯一的
在数组中使用的 keys 必须在它们的同辈之间唯一。然而它们并不需要全局唯一。我们可以在操作两个不同数组的时候使用相同的 keys。
表单(Forms)
-
受控组件(Controlled Components)
在 HTML 中,表单元素如
<input>
,<textarea>
和<select>
表单元素通常保持自己的状态,并根据用户输入进行更新。而在 React 中,可变状态一般保存在组件的 state(状态) 属性中,并且只能通过 setState() 更新。我们可以通过使 React 的 state 成为 “单一数据源原则” 来结合这两个形式。然后渲染表单的 React 组件也可以控制在用户输入之后的行为。这种形式,其 值由 React 控制的输入表单元素 称为“ 受控组件 ”。
例如,如果我们想在提交时记录名称,我们可以将表单写为受控组件:
class NameForm extends React.Component { constructor(props) { super(props); this.state = {value: ''}; } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert('A name was submitted: ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit.bind(this)}> <label> Name: <input type="text" value={this.state.value} onChange={this.handleChange.bind(this)} /> </label> <input type="submit" value="Submit" /> </form> ); } } ReactDOM.render( <NameForm />, document.getElementById('root') ); 复制代码
设置表单元素的value属性之后,其显示值将由
this.state.value
决定,以满足React状态的同一数据理念。每次键盘敲击之后会执行handleChange
方法以更新React状态,显示值也将随着用户的输入改变。由于
value
属性设置在我们的表单元素上,显示的值总是this.state.value
,以满足state
状态的同一数据理念。由于handleChange
在每次敲击键盘时运行,以更新 React state(状态),显示的值将更新为用户的输入。对于受控组件来说,每一次 state(状态) 变化都会伴有相关联的处理函数。这使得可以直接修改或验证用户的输入。
-
处理多个输入元素
当您需要处理多个受控的
input
元素时,您可以为每个元素添加一个name
属性,并且让处理函数根据event.target.name
的值来选择要做什么。而不必写多个onChange
函数 。例如:
class Reservation extends React.Component { constructor(props) { super(props); this.state = { isGoing: true, numberOfGuests: 2 }; this.handleInputChange = this.handleInputChange.bind(this); } handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); } render() { return ( <form> <label> Is going: <input name="isGoing" type="checkbox" checked={this.state.isGoing} onChange={this.handleInputChange} /> </label> <label> Number of guests: <input name="numberOfGuests" type="number" value={this.state.numberOfGuests} onChange={this.handleInputChange} /> </label> </form> ); } } ReactDOM.render( <Reservation />, document.getElementById('root') ); 复制代码
handleInputChange()
方法中,注意我们如何使用ES6计算的属性名称语法来更新与给定输入名称相对应的 state(状态) 键:const name = target.name; this.setState({ [name]: value }); 复制代码
状态提升(Lifting State Up)
-
在一个 React 应用中,对于任何可变的数据都应该循序“单一数据源”原则。通常情况下,state 首先被添加到需要它进行渲染的组件。然后,如果其它的组件也需要它,你可以提升状态到它们最近的祖先组件。你应该依赖 从上到下的数据流向 ,而不是试图在不同的组件中同步状态。
提升状态相对于双向绑定方法需要写更多的“模板”代码,但是有一个好处,它可以更方便的找到和隔离 bugs。由于任何 state(状态) 都 “存活” 在若干的组件中,而且可以分别对其独立修改,所以发生错误的可能大大减少。另外,你可以实现任何定制的逻辑来拒绝或者转换用户输入。
如果某个东西可以从 props(属性) 或者 state(状态) 得到,那么它可能不应该在 state(状态) 中。
(状态提升:子组件要渲染的数据不定义在自身
state
中,而是定义在最近的公共父组件中。该数据由父组件通过props
传递给子组件。当子组件发生改变时,触发父组件的onChange()
方法,父组件更改state
,引起子组件数据改变,从而达到A子组件数据变化时B子组件数据跟着变化的效果。)
组合 VS 继承(Composition vs Inheritance)
- 如果要在组件之间重用非 U I功能,我们建议将其提取到单独的 JavaScript 模块中。组件可以导入它并使用该函数,对象或类,而不扩展它。
React 的编程思想
-
如何拆分组件呢?
其实只需要像拆分一个新方法或新对象一样的方式即可。一个常用的技巧是 单一职责原则 ,即一个组件理想情况下只处理一件事。如果一个组件持续膨胀,就应该将其拆分为多个更小的组件中。
写在最后:强烈建议自己读一遍 React中文文档 (有些翻译错误,不影响阅读,有能力可以去读原版。 O(∩_∩)O哈哈~)