React 读书笔记(一)

最近又把 React 的官方文档和有关的书籍看了下,又有新的收获。下面记录下我最近有感受的或者觉得重要的一些地方。也体会到了其实最好的书籍就是官方文档。 而且React的文档写的很好,有很好的例子,有codepen,也有相应的解释说明。赞。


Adding React to an Existing Application

You don’t need to rewrite your app to start using React.

We recommend adding React to a small part of your application, such as an individual widget, so you can see if it works well for your use case.

如果已经有现有的 App,没有必要重头全部重构,可以先选取其中一个模块。


React Only Updates What’s Necessary

React DOM compares the element and its children to the previous one, and only applies the DOM updates necessary to bring the DOM to the desired state.

也就是传说中的虚拟DOM,每次render之前比较前后节点的区别,只更新需要更新的部分。


Components and Props

Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.

Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.

组件就像函数一样,接受 props ,然后返回对应的 元素。所以我的理解似乎组件一定要是可复用,简洁,并且保持独立。


Functional and Class Components

函数和 es6 的类都可以生成 Components。

1
2
3
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

This function is a valid React component because it accepts a single “props” object argument with data and returns a React element. We call such components “functional” because they are literally JavaScript functions.

You can also use an ES6 class to define a component:

1
2
3
4
5
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

can both be used as

1
2
3
4
5
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);


Rules to Follow

不要害怕把 大的 components 切分成小的。这一点我开始就有点害怕。总觉得分的大了就越开了,是不是越难维护了。但实际上只要组件确实是独立可复用,就完全可以大胆的分割。

Extracting Components
Don’t be afraid to split components into smaller components.

Props are Read-Only
React is pretty flexible but it has a single strict rule:

All React components must act like pure functions with respect to their props.

1
2
3
4
5
6
7
8
9
10
//pure, they do not attempt to change their inputs, and always return the same result for the same inputs.

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

//impure because it changes its own input
function withdraw(account, amount) {
account.total -= amount;
}

So,we need State, State allows React components to change their output over time in response to user actions, network responses, and anything else, without violating this rule.


Converting a Function to a Class

You can convert a functional component like Clock to a class in five steps:

  1. Create an ES6 class with the same name that extends React.Component.

  2. Add a single empty method to it called render().

  3. Move the body of the function into the render() method.

  4. Replace props with this.props in the render() body.

  5. Delete the remaining empty function declaration.

1
2
3
4
5
6
7
8
9
10
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

Components defined as classes have some additional features. Local state is exactly that: a feature available only to classes.


Adding Lifecycle Methods to a Class

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
37
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}

componentWillUnmount() {
clearInterval(this.timerID);
}

tick() {
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')
);

Using State Correctly

  1. Do Not Modify State Directly
    1
    2
    3
    4
    5
    // Wrong
    this.state.comment = 'Hello';

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

The only place where you can assign this.state is the constructor.

  1. State Updates May Be Asynchronous
    意思是state的更新可能是异步的,并且为了细嫩那个,有可能会合并setState。所以不能够依赖他们当前的值来计算下面的一个状态。
    React may batch multiple setState() calls into a single update for performance.

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

下面就使用了prevState来保存了当前的state,这样再计算,就不会因setState异步的原因,而导致结果错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});

// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));


// Correct
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
  1. The Data Flows Down
    Neither parent nor child components can know if a certain component is stateful or stateless, and they shouldn’t care whether it is defined as a function or a class.

This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it.


Handling Events

这里注意this的绑定方法有下面几种:
第一种:this.handleClick = this.handleClick.bind(this);

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
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};

// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}

render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}

ReactDOM.render(
<Toggle />,
document.getElementById('root')
);

第二种:使用arrow functions

1
2
3
handleClick = () => {
console.log('this is:', this);
}

我自己比较喜欢这种,简单方便。如果真的像第一种方法绑定, 。。总觉得写的代码不太干净的样子。

第三种,在callback中使用arror function

1
2
3
4
5
6
7
8
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}


Conditional Rendering

1
2
3
4
5
6
7
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}

也可以在render函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
render() {
const isLoggedIn = this.state.isLoggedIn;

let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}

return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}

也可以不用if else, 而用 condition ? true : false。


Preventing Component from Rendering

直接 return null,即可防止rendering.

In rare cases you might want a component to hide itself even though it was rendered by another component. To do this return null instead of its render output.

1
2
3
4
5
6
7
8
9
10
11
function WarningBanner(props) {
if (!props.warn) {
return null;
}

return (
<div className="warning">
Warning!
</div>
);
}

Lists and Keys

A “key” is a special string attribute you need to include when creating lists of elements.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<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')
);

Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity.

微信小程序貌似也是借用了这种方法。有key的时候渲染会快一点,性能会更好。

如果渲染的 obj 里面有唯一标示 id, 就用次 id, 如果没有,就用 index 也是可以的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//ok
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);

//ok
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);

keys需要注意的地方

Keys only make sense in the context of the surrounding array.

  1. keys 一定只能在需要它并且它确实在此处(上下文)有意义的时候再使用。
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
function ListItem(props) {
const value = props.value;
return (
// Wrong! There is no need to specify the key here:
<li key={value.toString()}>
{value}
</li>
);
}

function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Wrong! The key should have been specified here:
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);

正确的用法应该如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function ListItem(props) {
// Correct! There is no need to specify the key here:
return <li>{props.value}</li>;
}

function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Correct! Key should be specified inside the array.
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);

总结起来就是有 map 的后面就需要有个 key.

A good rule of thumb is that elements inside the map() call need keys.

  1. Keys Must Only Be Unique Among Siblings
    Keys used within arrays should be unique among their siblings. However they don’t need to be globally unique.

当然要是唯一的,如果不唯一他的本身的作用就失去了。只要在 map 里面 id 是唯一的即可。

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
function Blog(props) {
//key 1
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
//key 2
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}

const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('root')
);
  1. Keys serve as a hint to React but they don’t get passed to your components. If you need the same value in your component, pass it explicitly as a prop with a different name. keys 不作为上下文传递给component参数, 如果需要 key 值传给下面的 component 就显示的传递下即可。
1
2
3
4
5
6
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);

Lifting State Up

There should be a single “source of truth” for any data that changes in a React application. Usually, the state is first added to the component that needs it for rendering. Then, if other components also need it, you can lift it up to their closest common ancestor. Instead of trying to sync the state between different components, you should rely on the top-down data flow.

在React 应用中任何数据变动都应该是基于一个”source of truth”。通常state 被首先加入组件去渲染。然后,如果另一个组件也需要它,那么你就可以将它们提升到它们的父组件中。而不是尝试在不同的组件中同步state,你应该依赖top-down data flow。

提升状态涉及编写比双向绑定方法更多的‘样板’代码。但好处是找到和隔离bug需要较少的工作。由于任何状态存在于特定的组件中,并且该组件可以单独改变它,所以大大减少了错误的表面积。此外,你可以实现任何自定义逻辑以拒绝或转换用户输入。

如果数据可以从props或state派生,那么它就不应该在状态之中。例如,我们只存储了最后编辑的value和scale,而不是存储两个celsiusValue和fahrenheitValue。另一个输入的值总是可以从render()方法中计算出来。这允许我们清除或应用四舍五入到其他字段,而不会丢失用户输入的任何精度。