本文根据 Devhints / React.js 翻译。代码示例及版本范围沿用原文。原文标注更新日期:2020-07-05。
React 是一个用于构建用户界面的 JavaScript 库。本指南适用于 React v15 至 v16 版本。
组件
组件
import React from 'react'
import ReactDOM from 'react-dom'
class Hello extends React.Component {
render () {
return <div className='message-box'>
Hello {this.props.name}
</div>
}
}
const el = document.body
ReactDOM.render(<Hello name='John' />, el)
使用 React.js jsfiddle 开始尝试编写代码。(或者使用非官方的 jsbin)
导入多个导出项
import React, {Component} from 'react'
import ReactDOM from 'react-dom'
class Hello extends Component {
...
}
属性
<Video fullscreen={true} autoplay={false} />
render () {
this.props.fullscreen
const { fullscreen, autoplay } = this.props
···
}
使用 this.props 以访问传递给组件的属性。
参见: 属性
状态 (State)
constructor(props) {
super(props)
this.state = { username: undefined }
}
this.setState({ username: 'rstacruz' })
render () {
this.state.username
const { username } = this.state
···
}
使用状态 (this.state) 来管理动态数据。
With Babel 你可以使用 proposal-class-fields 从而无需编写构造函数 (constructor)
class Hello extends Component {
state = { username: undefined };
...
}
参见: 状态 (State)
嵌套 (Nesting)
class Info extends Component {
render () {
const { avatar, username } = this.props
return <div>
<UserAvatar src={avatar} />
<UserProfile username={username} />
</div>
}
}
从 React v16.2.0 版本开始,可以使用“片段”(fragments) 来返回多个子元素,而无需在 DOM 中添加额外的包裹节点。
import React, {
Component,
Fragment
} from 'react'
class Info extends Component {
render () {
const { avatar, username } = this.props
return (
<Fragment>
<UserAvatar src={avatar} />
<UserProfile username={username} />
</Fragment>
)
}
}
通过嵌套组件来实现关注点分离。
参见: 组件组合
子元素 (Children)
<AlertBox>
<h1>You have pending notifications</h1>
</AlertBox>
class AlertBox extends Component {
render () {
return <div className='alert-box'>
{this.props.children}
</div>
}
}
子元素通过 children 属性进行传递。
默认值
设置默认属性 (props)
Hello.defaultProps = {
color: 'blue'
}
参见: defaultProps
设置默认状态 (state)
class Hello extends Component {
constructor (props) {
super(props)
this.state = { visible: true }
}
}
在……中设置默认状态 constructor().
在不使用构造函数的情况下,通过…… Babel 使用 proposal-class-fields.
class Hello extends Component {
state = { visible: true }
}
参见: 设置默认状态
其他组件
函数式组件
function MyComponent ({ name }) {
return <div className='message-box'>
Hello {name}
</div>
}
函数式组件没有状态。此外,它们的 props 会作为函数的第一个参数传入。
参见: 函数式组件与类组件
纯组件 (Pure components)
import React, {PureComponent} from 'react'
class MessageBox extends PureComponent {
···
}
……的性能优化版本 React.Component。如果属性 (props) 或状态 (state) 没有发生变化,则不会重新渲染。已更改。
组件 API
this.forceUpdate()
this.setState({ ... })
this.setState(state => { ... })
this.state
this.props
以下方法和属性可用于 Component 实例。
参见: 组件 API
生命周期
挂载
| 方法 | 说明 |
|---|---|
constructor (props) | 渲染前 # |
componentWillMount() | 请勿在此处使用 # |
render() | 渲染 # |
componentDidMount() | 渲染后(DOM 可用) # |
componentWillUnmount() | 移除 DOM 前 # |
componentDidCatch() | 捕获错误 (16+) # |
在此处设置初始状态 constructor()在此处添加 DOM 事件处理程序、计时器等 componentDidMount(),并在相应位置将其移除 componentWillUnmount().
更新
| 方法 | 说明 |
|---|---|
componentDidUpdate (prevProps, prevState, 快照) | 使用 setState() 在此处执行,但请记得比较 props |
shouldComponentUpdate (newProps, newState) | 跳过 render() 如果返回 false |
render() | 渲染 |
componentDidUpdate (prevProps, prevState) | 在此处对 DOM 进行操作 |
当父组件更改属性(props)时会被调用, .setState()且在首次渲染时不会被调用。
参见: 组件规范
Hook(新增)
状态 Hook
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Hook 是 React 16.8 版本中新增的功能。
参见: Hook 概览
声明多个状态变量
import React, { useState } from 'react';
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
Effect Hook(副作用 Hook)
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
如果你熟悉 React 的类组件生命周期方法,可以将 useEffect Hook 理解为 componentDidMount, componentDidUpdate、 componentWillUnmount 等方法的组合。
默认情况下,React 会在每次渲染后执行副作用(effect)——包括首次渲染。
构建自定义 Hook
定义 FriendStatus
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}, [props.friend.id]);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
副作用还可以通过返回一个函数,来指定如何在执行后进行“清理”操作。
使用 FriendStatus
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
参见: 构建自定义 Hook
Hook API 参考
另请参阅: Hook FAQ
基础 Hook
| Hook | 说明 |
|---|---|
useState(initialState) | |
useEffect(() => { … }) | |
useContext(MyContext) | 返回的值 React.createContext |
详细信息: 基础 Hook
其他 Hook
| Hook | 说明 |
|---|---|
useReducer(reducer, initialArg, init) | |
useCallback(() => { … }) | |
useMemo(() => { … }) | |
useRef(initialValue) | |
useImperativeHandle(ref, () => { … }) | |
useLayoutEffect | 与……相同 useEffect,但它会在所有 DOM 变更之后同步触发 |
useDebugValue(value) | 在 React 开发工具(DevTools)中为自定义 Hook 显示标签 |
详细信息: 其他 Hook
DOM 节点
参考资料
class MyComponent extends Component {
render () {
return <div>
<input ref={el => this.input = el} />
</div>
}
componentDidMount () {
this.input.focus()
}
}
允许访问 DOM 节点。
参见: Ref 与 DOM
DOM 事件
class MyComponent extends Component {
render () {
<input type="text"
value={this.state.value}
onChange={event => this.onChange(event)} />
}
onChange (event) {
this.setState({ value: event.target.value })
}
}
将 Pass 函数传递给属性,例如 onChange.
参见: 事件
其他功能
传递 Props
<VideoPlayer src="video.mp4" />
class VideoPlayer extends Component {
render () {
return <VideoEmbed {...this.props} />
}
}
将 Props 传递 src="..." 给子组件。
参见 传递 Props
顶层 API
React.createClass({ ... })
React.isValidElement(c)
ReactDOM.render(<Component />, domnode, [callback])
ReactDOM.unmountComponentAtNode(domnode)
ReactDOMServer.renderToString(<Component />)
ReactDOMServer.renderToStaticMarkup(<Component />)
还有其他 Hook,但这些是最常用的。
参见: React top 级 API
JSX 模式
样式简写
const style = { height: 10 }
return <div style={style}></div>
return <div style={{ margin: 0, padding: 0 }}></div>
参见: 行内样式
内部 HTML
function markdownify() { return "<p>...</p>"; }
<div dangerouslySetInnerHTML={{__html: markdownify()}} />
参见: 危险地设置 innerHTML
列表 (Lists)
class TodoList extends Component {
render () {
const { items } = this.props
return <ul>
{items.map(item =>
<TodoItem item={item} key={item.key} />)}
</ul>
}
}
务必提供 key 属性进行传递。
条件判断
<Fragment>
{showMyComponent
? <MyComponent />
: <OtherComponent />}
</Fragment>
短路求值
<Fragment>
{showPopup && <Popup />}
...
</Fragment>
新特性
返回多个元素
你可以将多个元素作为数组或片段(fragments)返回。
数组
render () {
// Don't forget the keys!
return [
<li key="A">First item</li>,
<li key="B">Second item</li>
]
}
片段(Fragments)
render () {
return (
<Fragment>
<li>First item</li>
<li>Second item</li>
</Fragment>
)
}
参见: 片段与字符串
返回字符串
render() {
return 'Look ma, no spans!';
}
你可以直接返回一个字符串。
参见: 片段与字符串
错误
class MyComponent extends Component {
···
componentDidCatch (error, info) {
this.setState({ error })
}
}
通过……捕获错误 componentDidCatch. (React 16+)
参见: React 16 中的错误处理
传送门(Portals)
render () {
return React.createPortal(
this.props.children,
document.getElementById('menu')
)
}
这会将内容渲染 this.props.children 到 DOM 中的任意位置。
参见: 传送门(Portals)
客户端激活(Hydration)
const el = document.getElementById('app')
ReactDOM.hydrate(<App />, el)
使用 ReactDOM.hydrate 代替使用 ReactDOM.render 如果你是在……的输出之上进行渲染 ReactDOMServer.
参见: 水合 (Hydrate)
属性验证
PropTypes
import PropTypes from 'prop-types'
| Key(键/标识符) | 说明 |
|---|---|
any | 任意类型 |
基础
| Key(键/标识符) | 说明 |
|---|---|
string | |
number | |
func | 函数 |
bool | 布尔值(真或假) |
枚举 (Enum)
| Key(键/标识符) | 说明 |
|---|---|
oneOf(任意类型) | 枚举类型 |
oneOfType(类型数组) | 联合类型 |
数组
| Key(键/标识符) | 说明 |
|---|---|
array | |
arrayOf(…) |
对象
| Key(键/标识符) | 说明 |
|---|---|
object | |
objectOf(…) | 值均为特定类型的对象 |
instanceOf(…) | 类的实例 |
shape(…) |
元素
| Key(键/标识符) | 说明 |
|---|---|
element | React 元素 |
node | DOM 节点 |
必填
| Key(键/标识符) | 说明 |
|---|---|
(···).isRequired | 必填 |
基本类型
MyComponent.propTypes = {
email: PropTypes.string,
seats: PropTypes.number,
callback: PropTypes.func,
isClosed: PropTypes.bool,
any: PropTypes.any
}
必填类型
MyCo.propTypes = {
name: PropTypes.string.isRequired
}
元素
MyCo.propTypes = {
// React element
element: PropTypes.element,
// num, string, element, or an array of those
node: PropTypes.node
}
可枚举类型 (oneOf)
MyCo.propTypes = {
direction: PropTypes.oneOf([
'left', 'right'
])
}
数组与对象
MyCo.propTypes = {
list: PropTypes.array,
ages: PropTypes.arrayOf(PropTypes.number),
user: PropTypes.object,
user: PropTypes.objectOf(PropTypes.number),
message: PropTypes.instanceOf(Message)
}
MyCo.propTypes = {
user: PropTypes.shape({
name: PropTypes.string,
age: PropTypes.number
})
}
使用 .array[Of], .object[Of], .instanceOf, .shape.
自定义验证
MyCo.propTypes = {
customProp: (props, key, componentName) => {
if (!/matchme/.test(props[key])) {
return new Error('Validation failed!')
}
}
}
另请参阅
- React 网站 (reactjs.org)
- React 速查表 (reactcheatsheet.com)
- 优秀的 React 资源集合 (Awesome React) (github.com)
- React v0.14 速查表 旧版本