在 React 中,我们使用组件(有状态、可组合、可重用)来描述 UI 。在任何编程语言中,你都可以将组件视为简单的函数。
React 组件也一样, 它的输入是 props,输出是关于 UI 的描述。我们可以在多个 UI 中重用单个组件,组件也可以包含其他组件。React 组件的本质上就是一个普通的 JavaScript 函数。
尽管一些 React 组件是纯组件,但也可以在组件中引入副作用。例如,组件在浏览器中渲染时可能会更改网页的标题,或者可能会将浏览器视图滚动到某个位置。
最重要的是,React 组件可以拥有一个私有状态来保存在组件生命周期内可能发生变化的数据。这个私有状态驱动组件输出到原生 DOM 中!
为什么将 React 称为响应式设计?
当 React 组件的状态(它是其输入的一部分)发生更改时,它所代表的 UI (其输出)也会发生更改。UI 描述中的这种变化必须反映在我们正在使用的设备中。在浏览器中,我们需要更新 DOM 树。在 React 应用程序中,我们不会手动执行此操作。 state
更新时,React 自动响应,并在需要时自动(并有效)更新到 DOM 上。
函数组件
React 组件,最简单的形式就是 JavaScript 函数:
function Button (props) {
// 在这里返回一个DOM / React元素。例如:
return <button type="submit">{props.label}</button>;
}
// 在浏览器中渲染一个 Button 元素
ReactDOM.render(<Button label="Save" />, mountNode);
我们在 ReactDOM.render
中渲染 Button
组件,使用了类似 HTML 的样式,但它既不是 HTML,也不是 JS,甚至不是 React。这就是 JSX ,它是 JavaScript 的扩展,允许我们以类似于 HTML 的函数语法编写函数调用。
你可以尝试在 Button
函数内返回其他 HTML 元素,看看它们是如何被支持的(例如,返回 input
元素或 textarea
元素)。
JSX 不是 HTML
每个 JSX 元素只是调用 React.createElement(component, props, ...children)
的语法糖。因此,使用 JSX 可以完成的任何事情都可以通过纯 JS 完成。
上例就可以编写为不使用 JSX 的代码:
ReactDOM.render(
React.createElement(
Hello,
{name: 'Button'},
React.createElement(
'div',
null,
`Hello ${this.props.name}`,
)
),
document.getElementById('root'),
); // 在浏览器中渲染一个简单的 div 元素,显示 Hello Bottle
所以,在项目中运用 JSX,我们需要使用像 Babel 或 TypeScript 这样的转换器。例如,当 我们使用 create-react-app 创建项目时,就会在内部使用 Babel 来转换项目中的 JSX。
JSX 基本上是一种折中,使我们能够使用与 HTML 非常相似的语法,使用编译器将其转换为 React.createElement
调用,而不是直接使用 React.createElement
语法创建 React 组件。
React 组件是一个返回 React 元素的 JS 函数。当使用 JSX 时,<tag></tag>
语法会被转化为 React.createElement("tag")
。在创建 React 组件时应该牢记这一点。我们不是在写 HTML,而实在使用 JS 扩展来创建 React 元素(实际上是 JS 对象)的函数调用。
因此,JSX 允许我们类 HTML 的语法来表示 React 树,浏览器和 React 均不需要识别它,只有编译器才有。我们发送给浏览器的是无 JSX 代码。
命名必须以大写字母开头
请注意我们在上面例子中将组件命名为 Button
。第一个字母是大写字母,这是一个规定,因为我们在处理混合的 HTML 元素和 React 元素时,JSX 编译器(如 Babel )会将所有以小写字母开头的名称视为 HTML 元素。
- HTML 元素作为字符串传递给
React.createElement
调用 - React 元素需要作为变量传递
<button></button> // React.createElement("button", null)
<Button></Button> // React.createElement(Button, null)
再看一个例子:
function button () {
return <div>My Fancy Button</div>;
};
// 将会渲染一个 HTML button 组件
// 忽略 React 组件 button
ReactDOM.render(<button />, mountNode);
第一个参数是 props
的对象
像可以为 HTML 元素传递 id
或 title
等属性一样,React 元素在渲染时也可以接收属性列表。例如,上面的 Button
元素就接受了 一个 label
属性。在 React 中,React 元素接收的属性列表称为 props
。
使用函数组件时,你不必将包含属性列表的对象命名为 props
,但这是标准做法。但当我们使用类组件时,属性列表始终命名为 props
。
请注意,props
是可选的。有些组件可以没有 props
。但是,组件必须有返回值。React 组件不能返回 undefined
(显式或隐式)。它必须返回一个值。它可以返回 null
以使渲染器忽略其输出。
每当我使用 props
(或 state
)时,我喜欢使用对象解构。例如,Button
组件函数可以使用 props
解构写法:
const Button = ({ label }) => (
<button type="submit">{label}</button>
);
这种方法有许多好处,但最重要的是看上去方便,并确保组件不会收到任何其他不需要的额外 props
。
注意我这里使用的是 箭头函数 而不是常规函数。这只是我个人的一种风格偏好。有些人喜欢常规函数,这没有任何问题。我认为重要的是要与你选择的风格保持一致。