Appearance
React 学习
原文地址React
开始学习
1 React 全部都是组件化的
React 是围绕可重用组件的概念设计的。你定义小组件并将它们组合在一起形成更大的组件。
无论大小,所有组件都是可重用的,甚至在不同的项目中也是如此。
React 组件最简单的形式,就是一个普通的 JavaScript 函数:
react
function Button (props) {
// 这里返回一个 DOM 元素,例如:
return <button type="submit">{props.label}</button>;
}
// 将按钮组件呈现给浏览器
ReactDOM.render(<Button label="Save" />, mountNode)
例 1:编辑上面的代码并按 Ctrl+Enter 键执行(译者注:译文暂时没有这个功能,请访问原文使用此功能,下同)
- 括号中的 button 标签将稍后解释。现在不要担心它们。ReactDOM 也将稍后解释,但如果你想测试
- 这个例子和所有接下来的例子,上述 render 函数是必须的。(React 将要接管和控制的是
- ReactDOM.render 的第 2 个参数即目标 DOM 元素)。在 jsComplete REPL 中,你可以使用特殊的
- 变量 mountNode。
例 1 的注意事项:
- 组件名称首字母大写,Button。必须要这样做是因为我们将处理 HTML 元素和 React 元素的混合。小写名称是为 HTML 元素保留的。事实上,将 React 组件命名为 “button” 然后你就会发现 ReactDOM 会忽略这个函数,仅仅是将其作为一个普通的空 HTML 按钮来渲染。
- 每个组件都接收一个属性列表,就像 HTML 元素一样。在 React 中,这个列表被称为属性。虽然你可以将一个函数随意命名。
- 在上面 Button 函数组件的返回输出中,我们奇怪地写了段看上去像 HTML 的代码。这实际上既不是 JavaScript 也不是 HTML,老实说,这甚至不是 React.js。然而它非常流行,以至于成为 React 应用程序中的默认值。这就是所谓的 JSX,这是一个JavaScript 的扩展。JSX 也是一个折中方案!继续尝试并在上面的函数中返回其他 HTML 元素,看看它们是如何被支持的(例如,返回一个文本输入元素)。
2 JSX 输出的是什么?
上面的例 1 可以用没有 JSX 的纯 React.js 编写,如下:
react
function Button (props) {
return React.createElement(
"button",
{ type: "submit" },
props.label
);
}
// 要使用 Button,你可以这么做
ReactDOM.render(
React.createElement(Button, { label: "Save" }),
mountNode
);
例 2:不使用 JSX 编写 React 组件
在 React 顶级 API 中,createElement 函数是主函数。 这是你需要学习的 7 个 API 中的 1 个。React 的 API 就是这么小。
就像 DOM 自身有一个 document.createElement 函数来创建一个由标签名指定的元素一样, React 的 createElement 函数是一个高级函数,有和 document.createElement 同样的功能, 但它也可以用于创建一个表示 React 组件的元素。当我们使用上面例 2 中的按钮组件时,我们使用的是后者。
不像 document.createElement,React 的 createElement 在接收第二个参数后,接收一个动态参数, 它表示所创建元素的子元素。所以 createElement 实际上创建了一个树。
这里就是这样的一个例子:
react
const InputForm = React.createElement(
"form",
{ target: "_blank", action: "https://google.com/search" },
React.createElement("div", null, "Enter input and click Search"),
React.createElement("input", { className: "big-input" }),
React.createElement(Button, { label: "Search" })
);
// InputForm 使用 Button 组件,所以我们需要这样做:
function Button (props) {
return React.createElement(
"button",
{ type: "submit" },
props.label
);
}
// 然后我们可以通过 .render 方法直接使用 InputForm
ReactDOM.render(InputForm, mountNode);
3 你可以在 JavaScript 的任何地方使用 JSX
在 JSX 中,你可以在一对花括号内使用任何 JavaScript 表达式。
jsx
const RandomValue = () =>
<div>
{ Math.floor(Math.random() * 100) }
</div>;
// 使用:
ReactDOM.render(<RandomValue />, mountNode);
在 JSX 中使用 JavaScript 表达式
任何 JavaScript 表达式可以直接放在花括号中。这相当于在 JavaScript 中插入 ${} 模板
这是 JSX 内唯一的约束:只有表达式。例如,你不能使用 if 语句,但三元表达式是可以的
JavaScript 变量也是表达式,所以当组件接受属性列表时(不包括 RandomValue 组件,props 是可选择的),你可以在花括号里使用这些属性。我们在上述(例 1)的 Button 组件是这样使用的。
JavaScript 对象也是表达式。有些时候我们在花括号中使用 JavaScript 对象,这看起来像是使用了两个花括号,但是在花括号中确实只有一个对象。其中一个用例就是将 CSS 样式对象传递给响应中的特殊样式属性:
jsx
const ErrorDisplay = ({message}) =>
<div style={ { color: 'red', backgroundColor: 'yellow' } }>
{message}
</div>;
// 使用
ReactDOM.render(
<ErrorDisplay
message="These aren't the droids you're looking for"
/>,
mountNode
);
4 你可以使用 JavaScript 类写 React 组件
简单的函数组件非常适合简单的需求,但是有的时候我们需要的更多。React 也支持通过使用 JavaScript 类来创建组件。这里 Button 组件(在例 1 中)就是使用类的语法编写的。
jsx
class Button extends React.Component {
render() {
return <button>{this.props.label}</button>;
}
}
// 使用(相同的语法)
ReactDOM.render(<Button label="Save" />, mountNode);
使用 JavaScript 类创建组件
类的语法是非常简单的:定义一个扩展的 React.Component 类(另一个你需要学习的 React 的顶级 API)。 该类定义了一个单一的实例函数 —— render(),并使函数返回虚拟 DOM 对象。 每一次我们使用基于类的 Button 组件(例如,通过 <Button ... />), React 将从这个基于类的组件中实例化对象,并在 DOM 树中使用该对象。
这就是为什么上面的例子中我们可以在 JSX 中使用 this.props.label 渲染输出的原因,因为每一个组件都有一个特殊的称为 props 的 实例 属性,这让所有的值传递给该组件时被实例化
由于我们有一个与组件的单个使用相关联的实例,所以我们可以按照自己的意愿定制该实例。 例如,我们可以通过使用常规 JavaScript 构造函数来构造它:
jsx
class Button extends React.Component {
constructor(props) {
super(props);
this.id = Date.now();
}
render() {
return <button id={this.id}>{this.props.label}</button>;
}
}
// 使用
ReactDOM.render(<Button label="Save" />, mountNode);
5 React 中的事件:两个重要的区别
当处理 React 元素中的事件时,我们与 DOM API 的处理方式有两个非常重要的区别:
- 所有 React 元素属性(包括事件)都使用 camelCase 命名,而不是 lowercase。例如是 onClick 而不是 onclick。
- 我们将实际的 JavaScript 函数引用传递给事件处理程序,而不是字符串。例如是 onClick={handleClick} 而不是 onClick="handleClick"。
React 用自己的对象包装 DOM 对象事件以优化事件处理的性能,但是在事件处理程序内部, 我们仍然可以访问 DOM 对象上所有可以访问的方法。React 将经过包装的事件对象传递给每个调用函数。 例如,为了防止表单提交默认提交操作,你可以这样做
jsx
class Form extends React.Component {
handleSubmit = (event) => {
event.preventDefault();
console.log('Form submitted');
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
}
// 使用
ReactDOM.render(<Form />, mountNode);
6 每一个 React 组件都有一个故事:第 1 部分
- 首先,我们定义了一个模板来创建组件中的元素。
- 然后,我们在某处使用 React。例如,在 render 内部调用其他的组件,或者直接使用 ReactDOM.render。
- 然后,React 实例化一个对象然后给它设置 props 然后我们可以通过 this.props 访问。这些属性都是我们在第 2 步传入的。
- 因为这些全部都是 JavaScript,constructor 方法将会被调用(如果定义的话)。这是我们称之为的第一个:组件生命周期方法。
- 接下来 React 计算渲染之后的输出方法(虚拟 DOM 节点)。
- 因为这是 React 第一次渲染元素,React 将会与浏览器连通(代表我们使用 DOM API)来显示元素。这整个过程称为 mounting。
- 接下来 React 调用另一个生命周期函数,称为 componentDidMount。我们可以这样使用这个方法,例如:在 DOM 上做一些我们现在知道的在浏览器中存在的东西。在此生命周期方法之前,我们使用的 DOM 都是虚拟的。
- 一些组件的故事到此结束,其他组件得到卸载浏览器 DOM 中的各种原因。在后一种情况发生时,会调用另一个生命周期的方法,componentWillUnmount。
- 任何 mounted 的元素的状态都可能会改变。该元素的父级可能会重新渲染。无论哪种情况,mounted 的元素都可能接收到不同的属性集。React 的魔力就是这儿,我们实际上需要的正是 React 的这一点!在这一点之前,说实话,我们并不需要 React。
- 组价的故事还在继续,但是在此之前,我们需要理解我所说的这种状态。
7 React 组件可以具有私有状态
以下只适用于类组件。我有没有提到有人叫表象而已的部件 dumb?
状态类是任何 React 类组件中的一个特殊字段。React 检测每一个组件状态的变化,但是为了 React 更加有效,我们必须通过 React 的另一个 API 改变状态字段, 这就是我们要学习的另一个 API —— this.setState:
jsx
class CounterButton extends React.Component {
state = {
clickCounter: 0,
currentTimestamp: new Date(),
};
handleClick = () => {
this.setState((prevState) => {
return { clickCounter: prevState.clickCounter + 1 };
});
};
componentDidMount() {
setInterval(() => {
this.setState({ currentTimestamp: new Date() })
}, 1000);
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click</button>
<p>Clicked: {this.state.clickCounter}</p>
<p>Time: {this.state.currentTimestamp.toLocaleString()}</p>
</div>
);
}
}
// 使用
ReactDOM.render(<CounterButton />, mountNode);