React文档学习(2)

React文档学习(2)

  • 高级指南上半部分

深入JSX

在运行时选择类型

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
photo: PhotoStory,
video: VideoStory
};

function Story(props) {
// 正确!JSX 类型可以是一个以大写字母开头的变量.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}

对 JSX 类型使用点语法

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';

const MyComponents = {
DatePicker: function DatePicker(props) {
return <div>Imagine a {props.color} datepicker here.</div>;
}
}

function BlueDatePicker() {
return <MyComponents.DatePicker color="blue" />;
}

在xm项目中,写了很多{条件?<div></div>:<div></div>}这样的大量代码,其实可以写成点语法

JSX中的Children

Functions(函数) 作为 Children(子元素): props.children的值可以是回调函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Repeat(props) {
let items = [];
for (let i = 0; i < props.numTimes; i++) {
items.push(props.children(i)); // i会被作为index传入
}
return <div>{items}</div>;
}

function ListOfTenThings() {
return (
<Repeat numTimes={10}>
{(index) => <div key={index}>This is item {index} in the list</div>}
</Repeat>
);
}

使用PropTypes进行类型检查

要求单独的 Child

  • 使用 PropTypes.element ,你可以指定仅将单一子元素传递给组件,作为子节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import PropTypes from 'prop-types';

class MyComponent extends React.Component {
render() {
// 这里必须是一个元素,否则会发出警告。
const children = this.props.children;
return (
<div>
{children}
</div>
);
}
}
MyComponent.propTypes = {
children: PropTypes.element.isRequired
};

静态类型检查

  • Flow 是一个针对 JavaScript 代码的静态类型检查器。它是在Facebook开发的,经常和React一起使用。 它可以让你使用特殊的类型语法来注释变量,函数和React组件,并尽早地发现错误。
  • TypeScript 是一门由微软开发的编程语言。 它是 JavaScript 的一个类型超集,包含它自己的编译器。 作为一种类型化语言,Typescript 可以早在您的应用程序上线之前在构建时发现错误和错误。

Refs和DOM

添加Ref

  • Refs用于在常规数据流外强制修改子元素。被修改的子元素可以是 React 组件实例,或者是一个 DOM 元素。
  • 什么时候使用 refs 的场景:
  1. 处理focus、文本选择或者媒体播放
  2. 触发强制动画
  3. 集成第三方DOM库
  • ref 属性接受回调函数,并且当组件 装载(mounted) 或者 卸载(unmounted) 之后,回调函数会立即执行。

  • React 组件在加载时将 DOM 元素拥有一个current属性,在卸载时则会传入 null。在componentDidMountcomponentDidUpdate 这些生命周期回调之前执行 ref 回调。

  • 创建Refs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class MyComponent extends React.Component {
    constructor(props) {
    super(props);
    this.myRef = React.createRef();
    }
    render() {
    return <div ref={this.myRef} />;
    }
    }
  • 可以在dom元素和类组件上天界Ref,不能在函数式组件上使用 ref 属性,可以在函数式组件内部使用 ref来引用一个 DOM 元素或者 类(class)组件

对父组件暴露DOM节点

  • 从父组件访问子节点的 DOM 节点的途径

  • 适用于类组件和函数式组件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function CustomTextInput(props) {
    return (
    <div>
    <input ref={props.inputRef} />
    </div>
    );
    }

    class Parent extends React.Component {
    render() {
    return (
    <CustomTextInput
    inputRef={el => this.inputElement = el}
    />
    );
    }
    }

不受控组件

  • 受控组件,表单数据由React组件负责处理
  • 不受控组件,表单数据由DOM元素本身处理

优化性能

  • 可以通过重写生命周期函数 shouldComponentUpdate(nextProps,nextState) 来优化性能。
  • 继承React.PureComponent就不需要手动写shouldComponentUpdate,但是React.PureComponent仅仅会进行浅比较,所以如果 props 或者 state 可能会导致浅比较失败的情况下就不能使用 React.PureComponent 了。

不可变数据

  • 避免浅比较失败的方法是不要突变props或者state的值:

  • 方法1:使用…或者Object.assign({}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 向数组添加内容
    handleClick() {
    this.setState(prevState => ({
    words: [...prevState.words, 'marklar'],
    }));
    };
    // 更改对象的值
    function updateColorMap(colormap) {
    return Object.assign({}, colormap, {right: 'blue'});
    }
  • 方法2:使用 Immutable 数据结构

  • Immutable提供了通过结构共享实现(Structural Sharing)的、不可变的(Immutable)、原来集合在新集合创建之后仍然可用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 原本方法,x会因为y的变动而改变
    const x = { foo: 'bar' };
    const y = x;
    y.foo = 'baz';
    x === y; // true

    // 新的方法
    const SomeRecord = Immutable.Record({ foo: null });
    const x = new SomeRecord({ foo: 'bar' });
    const y = x.set('foo', 'baz');
    const z = x.set('foo', 'bar');
    x === y; // false
    x === z; // true

不使用ES6

  • mixin简单通俗的讲就是把一个对象的方法和属性拷贝到另一个对象上,注意这个继承还是有区别的。js是一种只支持单继承的语言,毕竟一个对象只有一个原型,如果想实现多继承,那就简单暴力的把需要继承的父类的所有属性都拷贝到子类上,就是使用mixin。
  • ES6不支持mixin,createReactClass可以允许使用 mixins

一致性比较

  • 无论什么时候,当根元素类型不同时,React 将会销毁原先的树并重写构建新的树。

  • keys 应该是稳定的、可预测的并且是唯一的。不稳定的 key (类似于 Math.random() 函数的结果)可能会产生非常多的组件实例并且 DOM 节点也会非必要性的重新创建。这将会造成极大的性能损失和组件内state的丢失。

    故在项目中采用拼接字符串的形式,而不是random来做

Context

  • getChildContext()和childContextTypes将方法或者变量写成全局的,子树中的任务组件都可以通过定义contextTypes访问到
  • 如果 contextTypes 在组件中定义,下列的生命周期方法将接受一个额外的参数——context 对象
  • 如果 contextTypes 作为函数的属性被定义,无状态的函数式组件也可以引用 context

Context更新而其组件不更新

1
2
3
4
5
6
7
8
9
10
11
12
13
class ThemeProvider extends React.Component {
// 当props中的color改变的时候,context会更新
getChildContext() {
return {color: this.props.color}
}
render() {
return <div>{this.props.children}</div>
}
}
// 添加color作为context
ThemeProvider.childContextTypes = {
color: React.PropTypes.string
}
1
2
3
4
5
6
7
8
9
10
11
class ThemedText extends React.Component {
render() {
return <div style={{color: this.context.color}}>
{this.props.children}
</div>
}
}
// 从context获得color渲染自己的组件
ThemedText.contextTypes = {
color: React.PropTypes.string
}
1
2
3
4
5
6
7
8
9
10
// 组件TodoList从props中获得todos属性,从而渲染出ThemedText组件
class TodoList extends React.PureComponent {
render() {
return (<ul>
{this.props.todos.map(todo =>
<li key={todo}><ThemedText>{todo}</ThemedText></li>
)}
</ul>)
}
}
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
const TODOS = ["Get coffee", "Eat cookies"]
class App extends React.Component {
constructor(p, c) {
super(p, c)
this.state = { color: "blue" }
}
render() {
return (
{/*ThemeProvider将color放到context中*/}
<ThemeProvider color={this.state.color}>
<button onClick={this.makeRed.bind(this)}>
{/*ThemedText通过this.context.color获取到父组件的color属性设置style*/}
<ThemedText>Red please!</ThemedText>
</button>
<TodoList todos={TODOS} />
</ThemeProvider>
)
}
//rewrite color attr in state
makeRed() {
this.setState({ color: "red" })
}
}
ReactDOM.render(
<App />,
document.getElementById("container")
)
  1. 当点击按钮的时候,按钮本身颜色发生变化,但是TodoList这个组件没有变化。因为它继承了React.PureComponent,组件的props没有发生变化,shouldComponentUpdate返回了false,不管是TodoList还是他的子组件都不会重新渲染。因此,我们的TodoList里面的所有的组件颜色都不会发生变化
  2. 更加糟糕的是,我们也不能在TodoList中手动实现shouldComponentUpdate(nextProps, nextState, nextContext),因为SCU根本就不会接收到context数据(只有当它的state或者props修改了才能接收到,getChildContext才会调用)。
  3. 因此,shouldComponentUpdate如果返回false,那么所有的子组件都不会接收到context的更新。如这里的TodoList组件返回了false,那么TodoList下所有的ThemedText从context中获取到的color都不会更新。