核心概念
Hello World
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
JSX 简介
const element = <h1>Hello, world!</h1>;
可以在大括号内放置任何有效的 JavaScript 表达式
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
React DOM 使用 camelCase
(小驼峰命名)来定义属性的名称。例如,JSX 里的 class
变成了 className
,而 tabindex
变为 tabIndex
。
React DOM 在渲染所有输入内容之前,默认会进行转义,防止注入攻击。
以下两种示例代码完全等效:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{ className: 'greeting' },
'Hello, world!'
);
元素渲染
<div id="root"></div>
const element = <h1>Hello, world</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
更新 UI 唯一的方式是创建一个全新的元素,并将其传入 ReactDOM.render()
。
React 只更新它需要更新的部分
组件 & Props
函数组件与 class 组件
两个组件在 React 里是等效的:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
渲染组件
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props
State & 生命周期
class Clock extends React.Component {
constructor(props) {
// 一定要记得调用父类构造函数
super(props);
this.state = { date: new Date() };
}
// 该方法会在组件已经被渲染到 DOM 中后运行
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
// 使用 setState 来更新 state ,React 会在该函数中更新状态,并重新 render ,
// 若使用 this.state.comment = 'Hello' 不会重新渲染组件
this.setState({
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')
);
当你调用 setState()
的时候,React 会把你提供的对象合并到当前的 state 。
组件可以选择把它的 state 作为 props 向下传递到它的子组件中:
<FormattedDate date={this.state.date} />
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
事件处理
React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
<button onClick={activateLasers}>Activate Lasers</button>
在 React 中你不能通过返回 false
的方式阻止默认行为。你必须显式调用 preventDefault()
:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>Click me</a>
);
}
使用 React 时,你一般不需要使用 addEventListener()
为已创建的 DOM 元素添加监听器。事实上,你只需要在该元素初始渲染的时候添加监听器即可。
在 JS 中,class 的方法默认不会绑定 this
。如果方法中用到了 this
,在构造方法中使用 bind()
函数进行绑定:
class Toggle extends React.Component {
constructor(props) {
super(props);
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
或者用 public class fields 语法( Create React App 默认启用此语法):
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>Click me</button>
);
}
}
条件渲染
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
与运算符 &&
在 JavaScript 中,true && expression
总是会返回 expression
, 而 false && expression
总是会返回 false
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
<!-- 条件渲染 -->
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
三目运算符
使用 JS 中的三目运算符 condition ? true : false
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
<!-- 三目运算符 -->
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
阻止组件渲染
你可以让 render()
方法直接返回 null
,而不进行任何渲染。
列表 & Key
// 这段代码生成了一个 1 到 5 的项目符号列表
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(number =>
// key 帮助 React 识别哪些元素改变了,比如被添加或删除
<li key={number.toString()}>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
一个元素的 key
最好是这个元素在列表中拥有的一个独一无二的字符串。
通常,我们使用数据中的 id
来作为元素的 key
。当元素没有确定 id
的时候,万不得已你可以使用元素索引 index
作为 key
。
元素的 key
只有放在就近的数组上下文中才有意义。
一个好的经验法则是:在 map()
方法中的元素需要设置 key
属性。
key
会传递信息给 React ,但不会传递给你的组件。如果你的组件中需要使用 key
属性的值,请用其他属性名显式传递这个值。
JSX 允许在大括号中嵌入任何表达式,所以我们可以内联 map()
返回的结果:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()} value={number} />
)}
</ul>
);
}
表单
使 React 的 state
成为 "唯一数据源" ,控制取值的表单输入元素的组件称为 "受控组件" 。
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('提交的名字: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
在 React 中,<textarea>
使用 value
属性代替文本。
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: 'coconut' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('你喜欢的风味是: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
选择你喜欢的风味:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">葡萄柚</option>
<option value="lime">酸橙</option>
<option value="coconut">椰子</option>
<option value="mango">芒果</option>
</select>
</label>
<input type="submit" value="提交" />
</form>
);
}
}
你可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项:
<select multiple={true} value={['B', 'C']}>
文件 input 标签
文件 input 见文件输入
处理多个输入
当需要处理多个 input
元素时,我们可以给每个元素添加 name
属性,并让处理函数根据 event.target.name
的值选择要执行的操作。
使用 ES6 计算属性名称的语法更新给定输入名称对应的 state 值:
this.setState({
[name]: value
});
受控输入空值
在受控组件上指定 value
会阻止用户更改输入。如果你指定了 value
,但输入仍可编辑,则可能是你意外地将 value
设置为 undefined
或 null
。
输入最初被锁定,但在短时间延迟后变为可编辑:
ReactDOM.render(<input value="hi" />, mountNode);
setTimeout(function() {
ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
受控组件的替代品
有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个 React 组件传递所有的输入 state 。你可能希望使用非受控组件, 这是实现输入表单的另一种方式。
成熟的解决方案
如果你想寻找包含验证、追踪访问字段以及处理表单提交的完整解决方案,使用 Formik 是不错的选择。
状态提升
组合 vs 继承
React 哲学
从设计稿开始
第一步:将设计好的 UI 划分为组件层级
第二步:用 React 创建一个静态版本
第三步:确定 UI state 的最小(且完整)表示
第四步:确定 state 放置的位置
第五步:添加反向数据流