React Doc

React 的官方文档 是个好东西,特别是 React 哲学, FAQ, Hooks FAQ

以下内容主要对应 文档-核心概念 章节

Props 的只读性

From
所有 React 组件都必须像纯函数一样保护它们的 props 不被更改

1
2
3
4
5
6
7
8
9
// 不允许
function withdraw(account, amount) {
account.total -= amount;
}

// 允许
function sum(a, b) {
return a + b;
}

正确使用 State(*3)

From

1. 不要直接修改 State

1
2
3
4
5
// Wrong 不会重新渲染
this.state.comment = 'Hello';

// Correct
this.setState({comment: 'Hello'});

2. State 的更新可能是异步的

出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用

@a 所以 “一个 state 依赖其他的 state/props” 的情况可能会出错

1
2
3
4
5
6
7
8
9
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});

// Correct @a 但是函数式组件用不了该方法(最多只能使用自己的上一个状态,多次更改同一个 state 时才有用);而且这种方法不符合 State 的使用
this.setState((state, props) => ({
counter: state.counter + props.increment
}));

3. State 的更新会被合并

@a 主要指的是 Class 组件中的 setState 方法,如果使用函数组件的 useSate() 方法的回调函数时需要手动合并内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}

componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});

fetchComments().then(response => {
this.setState({
comments: response.comments // 此时 this.state.posts 不变,this.state.comments 使用新值替换
});
});
}
}

// 函数组件,参考 https://zh-hans.reactjs.org/docs/hooks-reference.html#functional-updates 和 https://zh-hans.reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables
const [state, setState] = useState({});

setState(prevState => {
// 也可以使用 Object.assign
return {...prevState, ...updatedValues};
});
// useReducer 可能更适合用于管理包含多个子值的 state 对象。

State & Props

Form

React 哲学 中提到了什么时候不该用 State

元素的 key 只有放在就近的数组上下文中才有意义

From

一个好的经验法则是:在 map() 方法中的元素需要设置 key 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 错误做法
function ListItem(props) {
const value = props.value;
return (
// 错误!你不需要在这里指定 key:
<li key={value.toString()}>{value}</li>
);
}

function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 错误!元素的 key 应该在这里指定:
<ListItem value={number} />
);
return (
<ul>{listItems}</ul>
);
}

// 正确做法
function ListItem(props) {
// 正确!这里不需要指定 key:
return <li>{props.value}</li>;
}

function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 正确!key 应该在数组的上下文中被指定
<ListItem key={number.toString()} value={number} />
);
return (
<ul>{listItems}</ul>
);
}

受控组件/非受控组件

  • 受控

    使用 State 控制表单输入元素(如果觉得麻烦可以参考 生产方案

    @q 函数组件如何处理 多个输入 ?只能一个一个 useSate?

  • 非受控

    表单数据将交由 DOM 节点来处理,可以使用 ref 来处理

    @a 感觉像是建立一个链接,不过 ref 有使用限制(只能用于 DOM 元素或 Class 组件)

    • 不能给 函数组件 设置 ref 属性,具体参考 Refs 与函数组件
    • 函数组件内部自由使用,使用 useRef() Hook

受控还是非受控?

状态提升

From
@a 和 向下的数据流 有关

虽然提升 state 方式比双向绑定方式需要编写更多的“样板”代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于“存在”于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。

Props.children 属性

From

包含了调用组件时传递的 HTML 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}

function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">Welcome</h1>
<p className="Dialog-message">Thank you for visiting our spacecraft!</p>
</FancyBorder>
);
}

React 哲学

From
React 最棒的部分之一是引导我们思考如何构建一个应用。

  1. 将设计好的 UI 划分为组件层级

  2. 用 React 创建一个静态版本

    • 最好将渲染 UI 和添加交互这两个过程分开。这是因为,编写一个应用的静态版本时,往往要编写大量代码,而不需要考虑太多交互细节;添加交互功能时则要考虑大量细节,而不需要编写太多代码。
    • 在构建应用的静态版本时,我们需要创建一些会重用其他组件的组件,然后通过 props 传入所需的数据。props 是父组件向子组件传递数据的方式。即使你已经熟悉了 state 的概念,也完全不应该使用 state 构建静态版本。state 代表了随时间会产生变化的数据,应当仅在实现交互时使用。所以构建应用的静态版本时,你不会用到它。
  3. 确定 UI state 的最小(且完整)表示

    • 想要使你的 UI 具备交互功能,需要有触发基础数据模型改变的能力。React 通过实现 state 来完成这个任务。
    • 为了正确地构建应用,你首先需要找出应用所需的 state 的最小表示,并根据需要计算出其他所有数据。
    • 通过问自己以下三个问题,你可以逐个检查相应数据是否属于 state
      1. 该数据是否是由父组件通过 props 传递而来的?如果是,那它应该不是 state。
      2. 该数据是否随时间的推移而保持不变?如果是,那它应该也不是 state。
      3. 你能否根据其他 state 或 props 计算出该数据的值?如果是,那它也不是 state。
  4. 确定 state 放置的位置

    React 中的数据流是单向的,并顺着组件层级从上往下传递。哪个组件应该拥有某个 state 这件事,对初学者来说往往是最难理解的部分。尽管这可能在一开始不是那么清晰,但你可以尝试通过以下步骤来判断:

    1. 找到根据这个 state 进行渲染的所有组件。
    2. 找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)。
    3. 该共同所有者组件或者比它层级更高的组件应该拥有该 state。
    4. 如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置。
  5. 添加反向数据流

    React 通过一种比传统的双向绑定略微繁琐的方法来实现反向数据传递。尽管如此,但这种需要显式声明的方法更有助于人们理解程序的运作方式。

    @a 一般来说是在设置 useState 的更新函数或者 this.setState() 方法