reactjs

setState

1
2
3
// 用于更新用户界面以响应事件处理器和处理服务器数据的主要方式
setState(updater, callback)
setState(updater[, callback])

将对组件 state 的更改排入队列, 并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件

setState() 视为 请求 而不是立即更新组件的命令; 为了更好地感知性能, React 会延迟调用它, 然后通过一次传递更新多个组件; React 并不会保证 state 的变更会立即生效

如果需要调用 setState() 后立即读取 this.state, 则需要使用 componentDidUpdate 或 setState 的回调函数(setState(updater, callback)), 这两种方式都可以保证应用更新后触发

除非 shouldComponentUpdate() 返回 false, 否则 setState() 将始终执行重新渲染操作, 如果可变对象被使用, 且无法在 shouldComponentUpdate() 中实现条件渲染, 那么仅在新旧状态不一时调用 setState() 可以避免不必要的重新渲染

参数一为带有形参的 updater 函数

1
(state, props) => stateChange

state 是应用于变化时组件状态的引用, 他不可以直接被修改, 应该使用基于 state 和 props 构建的新对象来表示变化

updater 函数中接受的 state 和 props 都保证为最新, updater 的返回值会与 state 进行浅合并

setState() 第二个参数为可选的回调函数, 它将在 setState 完成合并并重新渲染组件执行, 通常建议使用 componentDidMount() 来代替此方案

参数一除了接受函数以外, 还可以接受对象类型

1
setState(stateChange[, callback])

stateChange 会将传入的对象浅层合并到新的 state 中; 这种形式的 setState() 也是异步的, 并且在同一周期内对多个 setState 进行批处理

1
2
3
4
5
6
7
8
9
10
11
12
this.setState({ quantity: 2 });
// 如果在同一周期内多次设置, 相当于:
Object.assign(
previousState,
{ quantity: state.quantity + 1 },
{ quantity: state.quantity + 1 },
// ...
)
// 后调用的 setState() 将覆盖同一周期先调用 setState 的值, 因此仅增加一次; 如果后续取决于当前状态, 建议使用 updater 函数的形式代替
this.setState((state) => {
return { quantity: state.quantity + 1 }
})

调用 setState, react 都做了哪些操作

在调用 setState 时, setState 会把 state 的变化插入队列, 告诉 react 这个组件及它的子组件需要根据更新后的 state 重新渲染, react 得到元素树后, 会自动计算新老树节点差异, 然后根据差异对界面进行最小化的重新渲染

在 dom-diff(差异算法)中, react 会相对精确的知道哪些位置发生变化及如何变化, 这就保证了按需更新, 而不是全部重新渲染

setState 的同异步问题

将 setState 看作为一个请求而不是一个立即执行的命令, 为了更好的性能, react 可能会延迟它, 把若干个更新放到一起执行, 所以在同步执行的过程中它是异步的, 反之则是同步的

forceUpdate

1
Component.forceUpdate(callback);

默认情况下, 当组件的 state 或 props 发生变化时, 组件将重新渲染, 如果 render() 方法依赖于其他数据, 则可以调用 forceUpdate() 强制让组件重新渲染

调用 forceUpdate() 将致使组件调用 render() 方法, 该操作将会跳过该组件的 shouldComponentUpdate(); 但其子组件会出发正常的生命周期, 包括 shouldComponentUpdate() 方法; 如果标记发生变化, React 仍将只更新 DOM

通常应该避免使用 forceUpdate(), 尽量在 render() 中使用 this.propsthis.state

React.createElement

原生 JS 简单实现 createElement

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
function createElement(type, props, children) {
props = props || {};
// 创建一个对象, 设置一些默认的属性值
let obj = {
type: null,
props: {
children: ''
},
ref: null,
key: null
};
// 用传递的 type 和 props 覆盖原有的默认值
obj = { ...obj, type, props: { ...props, children } };
// 把 ref 和 key 提取出来, 并删除 props 中的属性
'key' in obj.props
? (obj.key = obj.props.key, obj.props.key = undefined)
: null;
'ref' in obj.props
? (obj.ref = obj.props.ref, obj.props.ref = undefined)
: null;
return obj;
}

let objJSX = createElement(
'h1',
{ id: 'box', className: 'title', ref: 'A', key: 'V' }
'\u773E0'
);
console.log(objJSX);

React.cloneElement

ReactDOM.render

把创建的对象生成的 DOM 元素, 最后插入到页面中

原生 JS 简单实现 render

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
38
39
40
41
42
43
function render(obj = {}, container, callBack) {
let { type, props } = obj || {},
newElement = document.createElement(type);
for (let attr in props) {
// 不是私有的直接结束遍历
if (!props.hasOwnProperty(attr)) break;
// 如果当前属性没有值, 直接不处理即可
if (!props[attr]) continue;
let value = props[attr];
// 设置 className
if (attr === 'className') {
newElement.setAttribute('class', value);
continue;
}
// 设置 style
if (attr === 'style') {
if (value === '') continue;
for (let styleKey in value) {
if (value.hasOwnProperty(styleKey)) {
newElement['style'][styleKey] = value[styleKey]
}
}
continue;
}
// 设置 children
if (attr === 'children') {
if (!(value instanceof Arrat)) {
value = [value];
}
value.forEach((item, index) => {
if (typeof value === 'string') {
let text = document.createTextNode(value);
newElement.appendChild(text);
} else {
render(item, newElement)
}
})
}
newElement.setAttribute(attr, value)
}
container.appendChild(newElement);
callBack && callBack();
}

ReactDOM.createPortal

1
ReactDOM.createPortal(child, container)

创建 portal, Portal 将提供一种将子节点渲染到 DOM 节点中的方式, 该节点存在于 DOM 组件的层次结构之外

React.Children

React.Children 提供了用于处理 this.props.children 不透明数据结构的实用方法

React.Children.map

React.Children.forEach

React.Children.count

React.Children.only

React.Children.toArray