跳到主要内容

核心概念

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 & 生命周期

官方生命周期图表

React 生命周期

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 设置为 undefinednull

输入最初被锁定,但在短时间延迟后变为可编辑:

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 放置的位置

  • 第五步:添加反向数据流